Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Node parameterTypeNode = parameterTypes.getFirstChild(); for (Node astParameter : astParameters.children()) { Var var = functionScope.getVar(astParameter.getString()); Preconditions.checkNotNull(var); if (var.isTypeInferred() && var.getType() == unknownType) { JSType newType = null; if (iifeArgumentNode != null) { newType = iifeArgumentNode.getJSType(); } else if (parameterTypeNode != null) { newType = parameterTypeNode.getJSType(); } if (newType != null) { var.setType(newType); astParameter.setJSType(newType); } } if (parameterTypeNode != null) { parameterTypeNode = parameterTypeNode.getNext(); } if (iifeArgumentNode != null) { iifeArgumentNode = iifeArgumentNode.getNext(); } } } } } @Override FlowScope createInitialEstimateLattice() { return bottomScope; } @Override FlowScope createEntryLattice() { return functionScope; } @Override FlowScope flowThrough(Node n, FlowScope input) { // If we have not walked a path from <entry> to <n>, then we don't // want to infer anything about this scope. if (input == bottomScope) { return input; } FlowScope output = input.createChildFlowScope(); output = traverse(n, output); return output; } @Override @SuppressWarnings({"fallthrough", "incomplete-switch"}) List<FlowScope> branchedFlowThrough(Node source, FlowScope input) { // NOTE(nicksantos): Right now, we just treat ON_EX edges like UNCOND // edges. If we wanted to be perfect, we'd actually JOIN all the out // lattices of this flow with the in lattice, and then make that the out // lattice for the ON_EX edge. But it's probably too expensive to be // worthwhile. FlowScope output = flowThrough(source, input); Node condition = null; FlowScope conditionFlowScope = null; BooleanOutcomePair conditionOutcomes = null; List<DiGraphEdge<Node, Branch>> branchEdges = getCfg().getOutEdges(source); List<FlowScope> result = Lists.newArrayListWithCapacity(branchEdges.size()); for (DiGraphEdge<Node, Branch> branchEdge : branchEdges) { Branch branch = branchEdge.getValue(); FlowScope newScope = output; switch (branch) { case ON_TRUE: if (NodeUtil.isForIn(source)) { // item is assigned a property name, so its type should be string. Node item = source.getFirstChild(); Node obj = item.getNext(); FlowScope informed = traverse(obj, output.createChildFlowScope()); if (item.isVar()) { item = item.getFirstChild(); } if (item.isName()) { JSType iterKeyType = getNativeType(STRING_TYPE); ObjectType objType = getJSType(obj).dereference(); JSType objIndexType = objType == null ? null : objType.getTemplateTypeMap().

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Type, Node call) { if (fnType.getTemplateTypeMap().getTemplateKeys().isEmpty()) { return Collections.emptyMap(); } Map<TemplateType, JSType> resolvedTypes = Maps.newIdentityHashMap(); Node callTarget = call.getFirstChild(); if (NodeUtil.isGet(callTarget)) { Node obj = callTarget.getFirstChild(); maybeResolveTemplatedType( fnType.getTypeOfThis(), getJSType(obj), resolvedTypes); } if (call.hasMoreThanOneChild()) { maybeResolveTemplateTypeFromNodes( fnType.getParameters(), call.getChildAtIndex(1).siblings(), resolvedTypes); } return resolvedTypes; } private void maybeResolveTemplatedType( JSType paramType, JSType argType, Map<TemplateType, JSType> resolvedTypes) { if (paramType.isTemplateType()) { // @param {T} resolvedTemplateType( resolvedTypes, paramType.toMaybeTemplateType(), argType); } else if (paramType.isUnionType()) { // @param {Array.<T>|NodeList|Arguments|{length:number}} UnionType unionType = paramType.toMaybeUnionType(); for (JSType alernative : unionType.getAlternates()) { maybeResolveTemplatedType(alernative, argType, resolvedTypes); } } else if (paramType.isFunctionType()) { FunctionType paramFunctionType = paramType.toMaybeFunctionType(); FunctionType argFunctionType = argType .restrictByNotNullOrUndefined() .collapseUnion() .toMaybeFunctionType(); if (argFunctionType != null && argFunctionType.isSubtype(paramType)) { // infer from return type of the function type maybeResolveTemplatedType( paramFunctionType.getTypeOfThis(), argFunctionType.getTypeOfThis(), resolvedTypes); // infer from return type of the function type maybeResolveTemplatedType( paramFunctionType.getReturnType(), argFunctionType.getReturnType(), resolvedTypes); // infer from parameter types of the function type maybeResolveTemplateTypeFromNodes( paramFunctionType.getParameters(), argFunctionType.getParameters(), resolvedTypes); } } else if (paramType.isTemplatizedType()) { // @param {Array.<T>} ObjectType referencedParamType = paramType .toMaybeTemplatizedType() .getReferencedType(); JSType argObjectType = argType .restrictByNotNullOrUndefined() .collapseUnion(); if (argObjectType.isSubtype(referencedParamType)) { // If the argument type is a subtype of the parameter type, resolve any // template types amongst their templatized types. TemplateTypeMap paramTypeMap = paramType.getTemplateTypeMap(); TemplateTypeMap argTypeMap = argObjectType.getTemplateTypeMap(); for (TemplateType key : paramTypeMap.getTemplateKeys()) { maybeResolveTemplatedType( paramTypeMap.getTemplateType(key), argTypeMap.getTemplateType(key), resolvedTypes); } } } } private void maybeResolveTemplateTypeFromNodes( Iterable<Node> declParams, Iterable<Node> callParams, Map<TemplateType, JSType> resolvedTypes) { maybeResolveTemplateTypeFromNodes( declParams.iterator(), callParams.iterator(),

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> resolvedTypes); } private void maybeResolveTemplateTypeFromNodes( Iterator<Node> declParams, Iterator<Node> callParams, Map<TemplateType, JSType> resolvedTypes) { while (declParams.hasNext() && callParams.hasNext()) { Node declParam = declParams.next(); maybeResolveTemplatedType( getJSType(declParam), getJSType(callParams.next()), resolvedTypes); if (declParam.isVarArgs()) { while (callParams.hasNext()) { maybeResolveTemplatedType( getJSType(declParam), getJSType(callParams.next()), resolvedTypes); } } } } private static void resolvedTemplateType( Map<TemplateType, JSType> map, TemplateType template, JSType resolved) { JSType previous = map.get(template); if (!resolved.isUnknownType()) { if (previous == null) { map.put(template, resolved); } else { JSType join = previous.getLeastSupertype(resolved); map.put(template, join); } } } private static class TemplateTypeReplacer extends ModificationVisitor { private final Map<TemplateType, JSType> replacements; private final JSTypeRegistry registry; boolean madeChanges = false; TemplateTypeReplacer( JSTypeRegistry registry, Map<TemplateType, JSType> replacements) { super(registry); this.registry = registry; this.replacements = replacements; } @Override public JSType caseTemplateType(TemplateType type) { madeChanges = true; private FlowScope traverseNew(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node constructor = n.getFirstChild(); JSType constructorType = constructor.getJSType(); JSType type = null; if (constructorType != null) { constructorType = constructorType.restrictByNotNullOrUndefined(); if (constructorType.isUnknownType()) { type = unknownType; } else { FunctionType ct = constructorType.toMaybeFunctionType(); if (ct == null && constructorType instanceof FunctionType) { // If constructorType is a NoObjectType, then toMaybeFunctionType will // return null. But NoObjectType implements the FunctionType // interface, precisely because it can validly construct objects. ct = (FunctionType) constructorType; } if (ct != null && ct.isConstructor()) { backwardsInferenceFromCallSite(n, ct); // If necessary, create a TemplatizedType wrapper around the instance // type, based on the types of the constructor parameters. ObjectType instanceType = ct.getInstanceType(); Map<TemplateType, JSType> inferredTypes = inferTemplateTypesFromParameters(ct, n); if (inferredTypes.isEmpty()) { type = instanceType; } else { type = registry.createTemplatizedType(instanceType, inferredTypes); } } } } n.setJSType(type); return scope; } private BooleanOutcomePair traverseAnd(Node n, FlowScope scope) { return traverseShortCircuitingBinOp(n, scope, true); } private FlowScope traverseChildren(Node n, FlowScope scope

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Verifies that constants are only assigned a value once. * e.g. var XX = 5; * XX = 3; // error! * XX++; // error! * */ class ConstCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = DiagnosticType.error( "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", "constant {0} assigned a value more than once"); private final AbstractCompiler compiler; private final Set<Scope.Var> initializedConstants; /** * Creates an instance. */ public ConstCheck(AbstractCompiler compiler) { this.compiler = compiler; this.initializedConstants = new HashSet<Scope.Var>(); } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (parent != null && parent.isVar() && n.hasChildren()) { String name = n.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Map UNIQUE = new UniqueRenamingToken(); private static class UniqueRenamingToken implements RenamingMap { @Override public String get(String value) { return null; } } public ReplaceIdGenerators( AbstractCompiler compiler, Map<String, RenamingMap> idGens, boolean generatePseudoNames, String previousMapSerialized) { this.compiler = compiler; this.generatePseudoNames = generatePseudoNames; nameGenerators = Maps.newLinkedHashMap(); idGeneratorMaps = Maps.newLinkedHashMap(); consistNameMap = Maps.newLinkedHashMap(); Map<String, BiMap<String, String>> previousMap; previousMap = parsePreviousResults(previousMapSerialized); this.previousMap = previousMap; if (idGens != null) { for (Entry<String, RenamingMap> gen : idGens.entrySet()) { String name = gen.getKey(); RenamingMap map = gen.getValue(); if (map instanceof UniqueRenamingToken) { nameGenerators.put(name, createNameSupplier( RenameStrategy.INCONSISTENT, previousMap.get(name))); } else { nameGenerators.put(name, createNameSupplier( RenameStrategy.MAPPED, map)); } idGeneratorMaps.put(name, Maps.<String, String>newLinkedHashMap()); } } } enum RenameStrategy { CONSISTENT, INCONSISTENT, MAPPED, STABLE } private static interface NameSupplier { String getName(String id, String name); RenameStrategy getRenameStrategy(); } private static class ObfuscatedNameSuppier implements NameSupplier { private final NameGenerator generator; private final Map<String, String> previousMappings; private RenameStrategy renameStrategy; public ObfuscatedNameSuppier( RenameStrategy renameStrategy, BiMap<String, String> previousMappings) { this.previousMappings = previousMappings.inverse(); this.generator = new NameGenerator(previousMappings.keySet(), "", null); this.renameStrategy = renameStrategy; } @Override public String getName(String id, String name) { String newName = previousMappings.get(id); if (newName == null) { newName = generator.generateNextName(); } return newName; } @Override public RenameStrategy getRenameStrategy() { return renameStrategy; } } private static class PseudoNameSuppier implements NameSupplier { private int counter = 0; private RenameStrategy renameStrategy; public PseudoNameSuppier(RenameStrategy renameStrategy) { this.renameStrategy = renameStrategy; } @Override public String getName(String id, String name) { if (renameStrategy == RenameStrategy.INCONSISTENT) { return name + "$" + counter++; } return name + "$0"; } @Override public RenameStrategy getRenameStrategy() { return renameStrategy; } } private static class StableNameSupplier implements NameSupplier { @Override public String getName(String id, String name) { return Base64.base64EncodeInt(name.hashCode()); } @Override public RenameStrategy getRenameStrategy() { return RenameStrategy.STABLE;

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } } private static class MappedNameSupplier implements NameSupplier { private final RenamingMap map; MappedNameSupplier(RenamingMap map) { this.map = map; } @Override public String getName(String id, String name) { return map.get(name); } @Override public RenameStrategy getRenameStrategy() { return RenameStrategy.MAPPED; } } private NameSupplier createNameSupplier( RenameStrategy renameStrategy, BiMap<String, String> previousMappings) { previousMappings = previousMappings != null ? previousMappings : ImmutableBiMap.<String, String>of(); if (renameStrategy == RenameStrategy.STABLE) { return new StableNameSupplier(); } else if (generatePseudoNames) { return new PseudoNameSuppier(renameStrategy); } else { return new ObfuscatedNameSuppier(renameStrategy, previousMappings); } } private NameSupplier createNameSupplier( RenameStrategy renameStrategy, RenamingMap mappings) { Preconditions.checkState(renameStrategy == RenameStrategy.MAPPED); return new MappedNameSupplier(mappings); } private class GatherGenerators extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo doc = n.getJSDocInfo(); if (doc == null) { return; } int numGeneratorAnnotations = (doc.isConsistentIdGenerator() ? 1 : 0) + (doc.isIdGenerator() ? 1 : 0) + (doc.isStableIdGenerator() ? 1 : 0) + (doc.isMappedIdGenerator() ? 1 : 0); if (numGeneratorAnnotations == 0) { return; } else if (numGeneratorAnnotations > 1) { compiler.report(t.makeError(n, CONFLICTING_GENERATOR_TYPE)); } String name = null; if (n.isAssign()) { name = n.getFirstChild().getQualifiedName(); } else if (n.isVar()) { name = n.getFirstChild().getString(); } else if (n.isFunction()){ name = n.getFirstChild().getString(); if (name.isEmpty()) { return; } } if (doc.isConsistentIdGenerator()) { consistNameMap.put(name, Maps.<String, String>newLinkedHashMap()); nameGenerators.put( name, createNameSupplier( RenameStrategy.CONSISTENT, previousMap.get(name))); } else if (doc.isStableIdGenerator()) { nameGenerators.put( name, createNameSupplier( RenameStrategy.STABLE, previousMap.get(name))); } else if (doc.isIdGenerator()) { nameGenerators.put( name, createNameSupplier( RenameStrategy.INCONSISTENT, previousMap.get(name))); } else if (doc.isMappedIdGenerator()) { NameSupplier supplier = nameGenerators.get(name); if (supplier == null || supplier.getRenameStrategy() != RenameStrategy.MAPPED) { compiler.report(t.makeError(n, MISSING_NAME_MAP

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>_FOR_GENERATOR)); // skip registering the name in the list of Generators if there no // mapping. return; } } else { throw new IllegalStateException("unexpected"); } idGeneratorMaps.put(name, Maps.<String, String>newLinkedHashMap()); } } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new GatherGenerators()); if (!nameGenerators.isEmpty()) { NodeTraversal.traverse(compiler, root, new ReplaceGenerators()); } } private class ReplaceGenerators extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isCall()) { return; } String callName = n.getFirstChild().getQualifiedName(); NameSupplier nameGenerator = nameGenerators.get(callName); if (nameGenerator == null) { return; } if (!t.inGlobalScope() && nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) { // Warn about calls not in the global scope. compiler.report(t.makeError(n, NON_GLOBAL_ID_GENERATOR_CALL)); return; } if (nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) { for (Node ancestor : n.getAncestors()) { if (NodeUtil.isControlStructure(ancestor)) { // Warn about conditional calls. compiler.report(t.makeError(n, CONDITIONAL_ID_GENERATOR_CALL)); return; } } } Node arg = n.getFirstChild().getNext(); if (arg.isString()) { String rename = getObfuscatedName( arg, callName, nameGenerator, arg.getString()); parent.replaceChild(n, IR.string(rename)); compiler.reportCodeChange(); } else if (arg.isObjectLit()) { for (Node key : arg.children()) { String rename = getObfuscatedName( key, callName, nameGenerator, key.getString()); key.setString(rename); // Prevent standard renaming by marking the key as quoted. key.putBooleanProp(Node.QUOTED_PROP, true); } arg.detachFromParent(); parent.replaceChild(n, arg); compiler.reportCodeChange(); } // TODO(user): Error on id not a string or object literal. } private String getObfuscatedName( Node id, String callName, NameSupplier nameGenerator, String name) { String rename = null; Map<String, String> idGeneratorMap = idGeneratorMaps.get(callName); String instanceId = getIdForGeneratorNode( nameGenerator.getRenameStrategy() != RenameStrategy.INCONSISTENT, id); if (nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT) { Map<String, String> entry = consistNameMap.get(callName); rename = entry.get(instanceId); if (rename == null) { rename = nameGenerator.getName(instanceId, name); entry.put(instanceId, rename); } } else { rename = nameGenerator.getName(instanceId, name); } idGeneratorMap.put(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>rename, instanceId); return rename; } } /** * @return The serialize map of generators and their ids and their * replacements. */ public String getSerializedIdMappings() { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, Map<String, String>> replacements : idGeneratorMaps.entrySet()) { if (!replacements.getValue().isEmpty()) { sb.append("["); sb.append(replacements.getKey()); sb.append("]\n\n"); for (Map.Entry<String, String> replacement : replacements.getValue().entrySet()) { sb.append(replacement.getKey()); sb.append(':'); sb.append(replacement.getValue()); sb.append("\n"); } sb.append("\n"); } } return sb.toString(); } private Map<String, BiMap<String, String>> parsePreviousResults( String serializedMap) { // // The expected format looks like this: // // [generatorName] // someId:someFile:theLine:theColumn // // if (serializedMap == null || serializedMap.isEmpty()) { return Collections.emptyMap(); } Map<String, BiMap<String, String>> resultMap = Maps.newHashMap(); BufferedReader reader = new BufferedReader(new StringReader(serializedMap)); BiMap<String, String> currentSectionMap = null; String line; int lineIndex = 0; try { while ((line = reader.readLine()) != null) { lineIndex++; if (line.isEmpty()) { continue; } if (line.charAt(0) == '[') { String currentSection = line.substring(1, line.length() - 1); currentSectionMap = resultMap.get(currentSection); if (currentSectionMap == null) { currentSectionMap = HashBiMap.create(); resultMap.put(currentSection, currentSectionMap); } else { reportInvalidLine(line, lineIndex); return Collections.emptyMap(); } } else { int split = line.indexOf(':'); if (split != -1) { String name = line.substring(0, split); String location = line.substring(split + 1, line.length()); currentSectionMap.put(name, location); } else { reportInvalidLine(line, lineIndex); return Collections.emptyMap(); } } } } catch (IOException e) { JSError.make(INVALID_GENERATOR_ID_MAPPING, e.getMessage()); } return resultMap; } private void reportInvalidLine(String line, int lineIndex) { JSError.make(INVALID_GENERATOR_ID_MAPPING, "line(" + line + "): " + lineIndex); } String getIdForGeneratorNode(boolean consistent, Node n) { Preconditions.checkState(n.isString() || n.isStringKey()); if (consistent) { return n.getString(); } else { return n.getSourceFileName() + ':' + n.getLineno() + ":" + n.getCharno(); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import java.util.List; import java.util.Set; /** * This pass walks the AST to create a Collection of 'new' nodes and * 'goog.require' nodes. It reconciles these Collections, creating a * warning for each discrepancy. * */ class CheckRequiresForConstructors implements HotSwapCompilerPass { private final AbstractCompiler compiler; private final CodingConvention codingConvention; private final CheckLevel level; // Warnings static final DiagnosticType MISSING_REQUIRE_WARNING = DiagnosticType.disabled( "JSC_MISSING_REQUIRE_WARNING", "''{0}'' used but not goog.require''d"); CheckRequiresForConstructors(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.codingConvention = compiler.getCodingConvention(); this.level = level; } /** * Uses Collections of new and goog.require nodes to create a compiler warning * for each new class name without a corresponding goog.require(). */ @Override public void process(Node externs, Node root) { Callback callback = new CheckRequiresForConstructorsCallback(); new NodeTraversal(compiler, callback).traverseRoots(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Callback callback = new CheckRequiresForConstructorsCallback(); new NodeTraversal(compiler, callback).traverseWithScope(scriptRoot, SyntacticScopeCreator.generateUntypedTopScope(compiler)); } // Return true if the name is a class name (starts with an uppercase // character, but is not in all caps). private static boolean isClassName(String name) { return (name != null && name.length() > 1 && Character.isUpperCase(name.charAt(0)) && !name.equals(name.toUpperCase())); } // Return the shortest prefix of the className that refers to a class, // or null if no part refers to a class. private static String getOutermostClassName

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>(String className) { for (String part : className.split("\\.")) { if (isClassName(part)) { return className.substring(0, className.indexOf(part) + part.length()); } } return null; } /** * This class "records" each constructor and goog.require visited and creates * a warning for each new node without an appropriate goog.require node. * */ private class CheckRequiresForConstructorsCallback implements Callback { private final List<String> constructors = Lists.newArrayList(); private final List<String> requires = Lists.newArrayList(); private final List<Node> newNodes = Lists.newArrayList(); @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return parent == null || !parent.isScript() || !t.getInput().isExtern(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.ASSIGN: case Token.VAR: maybeAddConstructor(t, n); break; case Token.FUNCTION: // Exclude function expressions. if (NodeUtil.isStatement(n)) { maybeAddConstructor(t, n); } break; case Token.CALL: visitCallNode(n, parent); break; case Token.SCRIPT: visitScriptNode(t); break; case Token.NEW: visitNewNode(t, n); } } private void visitScriptNode(NodeTraversal t) { Set<String> classNames = Sets.newHashSet(); for (Node node : newNodes) { String className = node.getFirstChild().getQualifiedName(); String outermostClassName = getOutermostClassName(className); boolean notProvidedByConstructors = (constructors == null || !constructors.contains(className)); boolean notProvidedByRequires = (requires == null || (!requires.contains(className) && !requires.contains(outermostClassName))); if (notProvidedByConstructors && notProvidedByRequires && !classNames.contains(className)) { compiler.report( t.makeError(node, level, MISSING_REQUIRE_WARNING, className)); classNames.add(className); } } // for the next script, if there is one, we don't want the new, ctor, and // require nodes to spill over. this.newNodes.clear(); this.requires.clear(); this.constructors.clear(); } private void visitCallNode(Node n, Node parent) { String required = codingConvention.extractClassNameIfRequire(n, parent); if (required != null) { requires.add(required); } } private void visitNewNode(NodeTraversal t, Node n) { Node qNameNode = n.getFirstChild(); // If the ctor is something other than a qualified name, ignore it. if (!qNameNode.isQualifiedName()) { return; } // Grab the root ctor namespace. Node nameNode = qNameNode; for (; nameNode.hasChildren(); nameNode = nameNode.getFirstChild()) {} // We only consider programmer-defined constructors that are // global variables, or are defined on

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2012 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.jscomp.DefaultPassConfig.HotSwapPassFactory; import com.google.javascript.jscomp.GlobalVarReferenceMap.GlobalVarRefCleanupPass; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import java.util.List; /** * Provides passes that should be run before hot-swap/incremental builds. * * @author tylerg@google.com (Tyler Goodwin) */ class CleanupPasses extends PassConfig { private State state; public CleanupPasses(CompilerOptions options) { super(options); } @Override protected List<PassFactory> getChecks() { List<PassFactory> checks = Lists.newArrayList(); checks.add(fieldCleanupPassFactory); checks.add(scopeCleanupPassFactory); checks.add(globalVarRefCleanupPassFactory); return checks; } @Override protected State getIntermediateState() { return state; } @Override protected List<PassFactory> getOptimizations() { return ImmutableList.of(); } @Override protected void setIntermediateState(State state) { this.state = state; } final PassFactory fieldCleanupPassFactory = new HotSwapPassFactory("FieldCleaupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new FieldCleanupPass(compiler); } }; final PassFactory scopeCleanupPassFactory = new HotSwapPassFactory("ScopeCleanupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new MemoizedScopeCleanupPass(compiler); } }; final PassFactory globalVarRefCleanupPassFactory = new HotSwapPassFactory("GlobalVarRefCleanupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new GlobalVarRefCleanupPass(compiler); } }; /** * A CleanupPass implementation that will remove stored scopes from the * MemoizedScopeCreator of the compiler instance for a the hot swapped script. * <p> * This pass will also clear out Source Nodes of Function Types declared on * Vars tracked by Memoized

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>ScopeCreator */ static class MemoizedScopeCleanupPass implements HotSwapCompilerPass { private final AbstractCompiler compiler; public MemoizedScopeCleanupPass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { ScopeCreator creator = compiler.getTypedScopeCreator(); if (creator instanceof MemoizedScopeCreator) { MemoizedScopeCreator scopeCreator = (MemoizedScopeCreator) creator; String newSrc = scriptRoot.getSourceFileName(); for (Var var : scopeCreator.getAllSymbols()) { JSType type = var.getType(); if (type != null) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null && newSrc.equals(NodeUtil.getSourceName(fnType.getSource()))) { fnType.setSource(null); } } } scopeCreator.removeScopesForScript(originalRoot.getSourceFileName()); } } @Override public void process(Node externs, Node root) { // MemoizedScopeCleanupPass should not do work during process. } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2011 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticScope; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; /** * Helper classes for dealing with coding conventions. */ public class CodingConventions { private CodingConventions() {} /** Gets the default coding convention. */ public static CodingConvention getDefault() { return new DefaultCodingConvention(); } /** * A convention that wraps another. * * When you want to support a new library, you should subclass this * delegate, and override the methods that you want to customize. * * This way, a person using jQuery and Closure Library can create a new * coding convention by creating a jQueryCodingConvention that delegates * to a ClosureCodingConvention that delegates to a DefaultCodingConvention. */ public static class Proxy implements CodingConvention { protected final CodingConvention nextConvention; protected Proxy(CodingConvention convention) { this.nextConvention = convention; } @Override public boolean isConstant(String variableName) { return nextConvention.isConstant(variableName); } @Override public boolean isConstantKey(String keyName) { return nextConvention.isConstantKey(keyName); } @Override public boolean isValidEnumKey(String key) { return nextConvention.isValidEnumKey(key); } @Override public boolean isOptionalParameter(Node parameter) { return nextConvention.isOptionalParameter(parameter); } @Override public boolean isVarArgsParameter(Node parameter) { return nextConvention.isVarArgsParameter(parameter); } @Override public boolean isExported(String name, boolean local) { return nextConvention.isExported(name, local); } @Override public final boolean isExported(String name) { return isExported(name, false) || isExported(name, true); } @Override public boolean isPrivate(String name) { return nextConvention.isPrivate(name); } @Override public SubclassRelationship getClasses

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>DefinedByCall(Node callNode) { return nextConvention.getClassesDefinedByCall(callNode); } @Override public boolean isSuperClassReference(String propertyName) { return nextConvention.isSuperClassReference(propertyName); } @Override public String extractClassNameIfProvide(Node node, Node parent) { return nextConvention.extractClassNameIfProvide(node, parent); } @Override public String extractClassNameIfRequire(Node node, Node parent) { return nextConvention.extractClassNameIfRequire(node, parent); } @Override public String getExportPropertyFunction() { return nextConvention.getExportPropertyFunction(); } @Override public String getExportSymbolFunction() { return nextConvention.getExportSymbolFunction(); } @Override public List<String> identifyTypeDeclarationCall(Node n) { return nextConvention.identifyTypeDeclarationCall(n); } @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { nextConvention.applySubclassRelationship( parentCtor, childCtor, type); } @Override public String getAbstractMethodName() { return nextConvention.getAbstractMethodName(); } @Override public String getSingletonGetterClassName(Node callNode) { return nextConvention.getSingletonGetterClassName(callNode); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { nextConvention.applySingletonGetter( functionType, getterType, objectType); } @Override public boolean isInlinableFunction(Node n) { return nextConvention.isInlinableFunction(n); } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return nextConvention.getDelegateRelationship(callNode); } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { nextConvention.applyDelegateRelationship( delegateSuperclass, delegateBase, delegator, delegateProxy, findDelegate); } @Override public String getDelegateSuperclassName() { return nextConvention.getDelegateSuperclassName(); } @Override public void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions) { nextConvention.checkForCallingConventionDefiningCalls( n, delegateCallingConventions); } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope<JSType> scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions) { nextConvention.defineDelegateProxyPrototypeProperties( registry, scope, delegateProxyPrototypes, delegateCallingConventions); } @Override public String getGlobalObject() { return nextConvention.getGlobalObject(); } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return nextConvention.getAssertionFunctions(); } @Override public Bind describeFunctionBind(Node n) { return describeFunctionBind(n, false); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { return nextConvention.describeFunctionBind(n, useTypeInfo);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } @Override public boolean isPropertyTestFunction(Node call) { return nextConvention.isPropertyTestFunction(call); } @Override public boolean isPrototypeAlias(Node getProp) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { return nextConvention.getObjectLiteralCast(callNode); } @Override public Collection<String> getIndirectlyDeclaredProperties() { return nextConvention.getIndirectlyDeclaredProperties(); } } /** * The default coding convention. * Should be at the bottom of all proxy chains. */ private static class DefaultCodingConvention implements CodingConvention { private static final long serialVersionUID = 1L; @Override public boolean isConstant(String variableName) { return false; } @Override public boolean isConstantKey(String variableName) { return false; } @Override public boolean isValidEnumKey(String key) { return key != null && key.length() > 0; } @Override public boolean isOptionalParameter(Node parameter) { // be as lax as possible, but this must be mutually exclusive from // var_args parameters. return false; } @Override public boolean isVarArgsParameter(Node parameter) { // be as lax as possible return false; } @Override public boolean isExported(String name, boolean local) { return local && name.startsWith("$super"); } @Override public boolean isExported(String name) { return isExported(name, false) || isExported(name, true); } @Override public boolean isPrivate(String name) { return false; } @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { return null; } @Override public boolean isSuperClassReference(String propertyName) { return false; } @Override public String extractClassNameIfProvide(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String extractClassNameIfRequire(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String getExportPropertyFunction() { return null; } @Override public String getExportSymbolFunction() { return null; } @Override public List<String> identifyTypeDeclarationCall(Node n) { return null; } @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { // do nothing } @Override public String getAbstractMethodName() { return null; } @Override public String getSingletonGetterClassName(Node callNode) { return null; } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { // do nothing. } @Override public boolean isInlinableFunction(Node n) { Preconditions.checkState(n.isFunction()); return true; } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return null; } @Override public void apply

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>DelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { // do nothing. } @Override public String getDelegateSuperclassName() { return null; } @Override public void checkForCallingConventionDefiningCalls(Node n, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope<JSType> scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public String getGlobalObject() { return "window"; } @Override public boolean isPropertyTestFunction(Node call) { return false; } @Override public boolean isPrototypeAlias(Node getProp) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { return null; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return Collections.emptySet(); } @Override public Bind describeFunctionBind(Node n) { return describeFunctionBind(n, false); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { if (!n.isCall()) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name.equals("Function.prototype.bind.call")) { // goog.bind(fn, self, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = safeNext(fn); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } } if (callTarget.isGetProp() && callTarget.getLastChild().getString().equals("bind")) { Node maybeFn = callTarget.getFirstChild(); JSType maybeFnType = maybeFn.getJSType(); FunctionType fnType = null; if (useTypeInfo && maybeFnType != null) { fnType = maybeFnType.restrictByNotNullOrUndefined() .toMaybeFunctionType(); } if (fnType != null || maybeFn.isFunction()) { // (function(){}).bind(self, args...); Node thisValue = callTarget.getNext(); Node parameters = safeNext(thisValue); return new Bind(maybeFn, thisValue, parameters); } } return null; } @Override public Collection<String> getIndirectlyDeclaredProperties() { return ImmutableList.of(); } private Node safeNext(Node n) { if (n != null) { return n.getNext(); } return null; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.io.CharStreams; import com.google.common.io.Files; import com.google.javascript.rhino.jstype.StaticSourceFile; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.nio.charset.Charset; import java.util.Arrays; /** * An abstract representation of a source file that provides access to * language-neutral features. The source file can be loaded from various * locations, such as from disk or from a preloaded string. * * @author nicksantos@google.com (Nick Santos) */ public class SourceFile implements StaticSourceFile, Serializable { private static final long serialVersionUID = 1L; /** A JavaScript source code provider. The value should * be cached so that the source text stays consistent throughout a single * compile. */ public interface Generator { public String getCode(); } /** * Number of lines in the region returned by {@link #getRegion(int)}. * This length must be odd. */ private static final int SOURCE_EXCERPT_REGION_LENGTH = 5; private final String fileName; private boolean isExternFile = false; // The fileName may not always identify the original file - for example, // supersourced Java inputs, or Java inputs that come from Jar files. This // is an optional field that the creator of an AST or SourceFile can set. // It could be a path to the original file, or in case this SourceFile came // from a Jar, it could be the path to the Jar. private String originalPath = null; // Source Line Information private int[] lineOffsets = null; private String code = null; /** * Construct a new abstract source file. * * @param fileName The file name of the source file. It does not necessarily * need to correspond to a real path. But it should be unique. Will * appear in warning messages emitted by the compiler. */ public SourceFile(String fileName) { if (fileName == null || fileName.isEmpty()) { throw new IllegalArgumentException("a source must have a name"); } this.fileName = fileName; } @

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Override public int getLineOffset(int lineno) { findLineOffsets(); if (lineno < 1 || lineno > lineOffsets.length) { throw new IllegalArgumentException( "Expected line number between 1 and " + lineOffsets.length + "\nActual: " + lineno); } return lineOffsets[lineno - 1]; } /** @return The number of lines in this source file. */ int getNumLines() { findLineOffsets(); return lineOffsets.length; } private void findLineOffsets() { if (lineOffsets != null) { return; } try { String[] sourceLines = getCode().split("\n"); lineOffsets = new int[sourceLines.length]; for (int ii = 1; ii < sourceLines.length; ++ii) { lineOffsets[ii] = lineOffsets[ii - 1] + sourceLines[ii - 1].length() + 1; } } catch (IOException e) { lineOffsets = new int[1]; lineOffsets[0] = 0; } } ////////////////////////////////////////////////////////////////////////////// // Implementation /** * Gets all the code in this source file. * @throws IOException */ public String getCode() throws IOException { return code; } /** * Gets a reader for the code in this source file. */ public Reader getCodeReader() throws IOException { return new StringReader(getCode()); } @VisibleForTesting String getCodeNoCache() { return code; } private void setCode(String sourceCode) { code = sourceCode; } public String getOriginalPath() { return originalPath != null ? originalPath : fileName; } public void setOriginalPath(String originalPath) { this.originalPath = originalPath; } // For SourceFile types which cache source code that can be regenerated // easily, flush the cache. We maintain the cache mostly to speed up // generating source when displaying error messages, so dumping the file // contents after the compile is a fine thing to do. public void clearCachedSource() { // By default, do nothing. Not all kinds of SourceFiles can regenerate // code. } boolean hasSourceInMemory() { return code != null; } /** Returns a unique name for the source file. */ @Override public String getName() { return fileName; } /** Returns whether this is an extern. */ @Override public boolean isExtern() { return isExternFile; } /** Sets that this is an extern. */ void setIsExtern(boolean newVal) { isExternFile = newVal; } @Override public int getLineOfOffset(int offset) { findLineOffsets(); int search = Arrays.binarySearch(lineOffsets, offset); if (search >= 0) { return search + 1; // lines are 1-based. } else { int insertionPoint = -1 * (search + 1); return Math.min(insertionPoint - 1, lineOffsets.length - 1) + 1; } } @Override public int getColumnOfOffset(int offset) { int line = getLineOfOffset(offset);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> endLine, js.substring(pos, last)); } else { return new SimpleRegion(startLine, endLine, js.substring(pos)); } } else { return new SimpleRegion(startLine, endLine, js.substring(pos, end)); } } @Override public String toString() { return fileName; } public static SourceFile fromFile(String fileName, Charset c) { return builder().withCharset(c).buildFromFile(fileName); } public static SourceFile fromFile(String fileName) { return builder().buildFromFile(fileName); } public static SourceFile fromFile(File file, Charset c) { return builder().withCharset(c).buildFromFile(file); } public static SourceFile fromFile(File file) { return builder().buildFromFile(file); } public static SourceFile fromCode(String fileName, String code) { return builder().buildFromCode(fileName, code); } public static SourceFile fromCode(String fileName, String originalPath, String code) { return builder().withOriginalPath(originalPath) .buildFromCode(fileName, code); } public static SourceFile fromInputStream(String fileName, InputStream s) throws IOException { return builder().buildFromInputStream(fileName, s); } public static SourceFile fromInputStream(String fileName, String originalPath, InputStream s) throws IOException { return builder().withOriginalPath(originalPath) .buildFromInputStream(fileName, s); } public static SourceFile fromReader(String fileName, Reader r) throws IOException { return builder().buildFromReader(fileName, r); } public static SourceFile fromGenerator(String fileName, Generator generator) { return builder().buildFromGenerator(fileName, generator); } /** Create a new builder for source files. */ public static Builder builder() { return new Builder(); } /** * A builder interface for source files. * * Allows users to customize the Charset, and the original path of * the source file (if it differs from the path on disk). */ public static class Builder { private Charset charset = Charsets.UTF_8; private String originalPath = null; public Builder() {} /** Set the charset to use when reading from an input stream or file. */ public Builder withCharset(Charset charset) { this.charset = charset; return this; } /** Set the original path to use. */ public Builder withOriginalPath(String originalPath) { this.originalPath = originalPath; return this; } public SourceFile buildFromFile(String fileName) { return buildFromFile(new File(fileName)); } public SourceFile buildFromFile(File file) { return new OnDisk(file, originalPath, charset); } public SourceFile buildFromCode(String fileName, String code) { return new Preloaded(fileName, originalPath, code); } public SourceFile buildFromInputStream(String fileName, InputStream s) throws IOException { return buildFromCode(fileName, CharStreams.toString(new InputStreamReader(s, charset))); } public SourceFile buildFromReader(String fileName, Reader r) throws IOException { return buildFromCode(fileName, CharStreams.toString(r));

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } public SourceFile buildFromGenerator(String fileName, Generator generator) { return new Generated(fileName, originalPath, generator); } } ////////////////////////////////////////////////////////////////////////////// // Implementations /** * A source file where the code has been preloaded. */ static class Preloaded extends SourceFile { private static final long serialVersionUID = 1L; Preloaded(String fileName, String originalPath, String code) { super(fileName); super.setOriginalPath(originalPath); super.setCode(code); } } /** * A source file where the code will be dynamically generated * from the injected interface. */ static class Generated extends SourceFile { private static final long serialVersionUID = 1L; private final Generator generator; // Not private, so that LazyInput can extend it. Generated(String fileName, String originalPath, Generator generator) { super(fileName); super.setOriginalPath(originalPath); this.generator = generator; } @Override public synchronized String getCode() throws IOException { String cachedCode = super.getCode(); if (cachedCode == null) { cachedCode = generator.getCode(); super.setCode(cachedCode); } return cachedCode; } // Clear out the generated code when finished with a compile; we can // regenerate it if we ever need it again. @Override public void clearCachedSource() { super.setCode(null); } } /** * A source file where the code is only read into memory if absolutely * necessary. We will try to delay loading the code into memory as long as * possible. */ static class OnDisk extends SourceFile { private static final long serialVersionUID = 1L; private final File file; // This is stored as a String, but passed in and out as a Charset so that // we can serialize the class. // Default input file format for JSCompiler has always been UTF_8. private String inputCharset = Charsets.UTF_8.name(); OnDisk(File file, String originalPath, Charset c) { super(file.getPath()); this.file = file; super.setOriginalPath(originalPath); if (c != null) { this.setCharset(c); } } @Override public synchronized String getCode() throws IOException { String cachedCode = super.getCode(); if (cachedCode == null) { cachedCode = Files.toString(file, this.getCharset()); super.setCode(cachedCode); } return cachedCode; } /** * Gets a reader for the code in this source file. */ @Override public Reader getCodeReader() throws IOException { if (hasSourceInMemory()) { return super.getCodeReader(); } else { // If we haven't pulled the code into memory yet, don't. return new FileReader(file); } } // Flush the cached code after the compile; we can read it off disk // if we need it again. @Override public void clearCachedSource() { super.setCode(null); } /** * Store the Charset specification as the string version of the name, * rather than the Charset itself. This allows us to serialize the * SourceFile

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfoBuilder; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; /** * Checks for non side effecting statements such as * <pre> * var s = "this string is " * "continued on the next line but you forgot the +"; * x == foo(); // should that be '='? * foo();; // probably just a stray-semicolon. Doesn't hurt to check though * </p> * and generates warnings. * */ final class CheckSideEffects extends AbstractPostOrderCallback implements HotSwapCompilerPass { static final DiagnosticType USELESS_CODE_ERROR = DiagnosticType.warning( "JSC_USELESS_CODE", "Suspicious code. {0}"); static final String PROTECTOR_FN = "JSCOMPILER_PRESERVE"; private final CheckLevel level; private final List<Node> problemNodes = Lists.newArrayList(); private final AbstractCompiler compiler; private final boolean protectSideEffectFreeCode; CheckSideEffects(AbstractCompiler compiler, CheckLevel level, boolean protectSideEffectFreeCode) { this.compiler = compiler; this.level = level; this.protectSideEffectFreeCode = protectSideEffectFreeCode; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); // Code with hidden side-effect code is common, for example // accessing "el.offsetWidth" forces a reflow in browsers, to allow this // will still allowing local dead code removal in general, // protect the "side-effect free" code in the source. // if (protectSideEffectFreeCode) { protectSideEffects(); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // VOID nodes appear when there are extra semicolons at the BLOCK level. // I've been unable to think of any cases where this indicates a bug, // and apparently some people like keeping these semicolons around, // so we

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> class StripProtection extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; StripProtection(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall()) { Node target = n.getFirstChild(); // TODO(johnlenz): add this to the coding convention // so we can remove goog.reflect.sinkValue as well. if (target.isName() && target.getString().equals(PROTECTOR_FN)) { Node expr = n.getLastChild(); n.detachChildren(); parent.replaceChild(n, expr); } } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>(templateValues); this.registry = registry; this.templateKeys = templateKeys; int nKeys = templateKeys.size(); this.templateValues = templateValues.size() > nKeys ? templateValues.subList(0, nKeys) : templateValues; // Iteratively resolve any JSType values that refer to the TemplateType keys // of this TemplateTypeMap. TemplateTypeMapReplacer replacer = new TemplateTypeMapReplacer( registry, this); ImmutableList.Builder<JSType> builder = ImmutableList.builder(); for (JSType templateValue : this.templateValues) { builder.add(templateValue.visit(replacer)); } this.resolvedTemplateValues = builder.build(); } /** * Returns true if the map is empty; false otherwise. */ public boolean isEmpty() { return templateKeys.isEmpty(); } /** * Returns a list of all template keys. */ public ImmutableList<TemplateType> getTemplateKeys() { return templateKeys; } /** * Returns true if this map contains the specified template key, false * otherwise. */ public boolean hasTemplateKey(TemplateType templateKey) { // Note: match by identity, not equality for (TemplateType entry : templateKeys) { if (entry == templateKey) { return true; } } return false; } /** * Returns the number of template keys in this map that do not have a * corresponding JSType value. */ int numUnfilledTemplateKeys() { return templateKeys.size() - templateValues.size(); } /** * Returns a list of template keys in this map that do not have corresponding * JSType values. */ ImmutableList<TemplateType> getUnfilledTemplateKeys() { return templateKeys.subList(templateValues.size(), templateKeys.size()); } /** * Returns true if there is a JSType value associated with the specified * template key; false otherwise. */ public boolean hasTemplateType(TemplateType key) { return getTemplateTypeIndex(key) != -1; } /** * Returns the JSType value associated with the specified template key. If no * JSType value is associated, returns UNKNOWN_TYPE. */ public JSType getTemplateType(TemplateType key) { int index = getTemplateTypeIndex(key); return (index == -1) ? registry.getNativeType(JSTypeNative.UNKNOWN_TYPE) : templateValues.get(index); } public TemplateType getTemplateTypeKeyByName(String keyName) { for (TemplateType key : templateKeys) { if (key.getReferenceName().equals(keyName)) { return key; } } return null; } /** * Returns the index of the JSType value associated with the specified * template key. If no JSType value is associated, returns -1. */ private int getTemplateTypeIndex(TemplateType key) { int maxIndex = Math.min(templateKeys.size(), templateValues.size()); for (int i = maxIndex - 1; i >= 0; i--) { if (templateKeys.get(i) == key) { return i; } } return -1; }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> private JSType getResolvedTemplateType(TemplateType key) { int index = getTemplateTypeIndex(key); return (index == -1) ? registry.getNativeType(JSTypeNative.UNKNOWN_TYPE) : resolvedTemplateValues.get(index); } /** * An enum tracking the three different equivalence match states for a * template key-value pair. */ private enum EquivalenceMatch { NO_KEY_MATCH, VALUE_MISMATCH, VALUE_MATCH } /** * Determines if this map and the specified map have equivalent template * types. */ public boolean checkEquivalenceHelper( TemplateTypeMap that, EquivalenceMethod eqMethod) { ImmutableList<TemplateType> thisKeys = getTemplateKeys(); ImmutableList<TemplateType> thatKeys = that.getTemplateKeys(); EquivalenceMatch[] thatMatches = new EquivalenceMatch[thatKeys.size()]; Arrays.fill(thatMatches, EquivalenceMatch.NO_KEY_MATCH); for (int i = 0; i < thisKeys.size(); i++) { TemplateType thisKey = thisKeys.get(i); JSType thisType = getResolvedTemplateType(thisKey); EquivalenceMatch thisMatch = EquivalenceMatch.NO_KEY_MATCH; for (int j = 0; j < thatKeys.size(); j++) { TemplateType thatKey = thatKeys.get(j); JSType thatType = that.getResolvedTemplateType(thatKey); // Cross-compare every key-value pair in this TemplateTypeMap with // those in that TemplateTypeMap. Update the Equivalence match for both // key-value pairs involved. if (thisKey == thatKey) { EquivalenceMatch newMatchType = EquivalenceMatch.VALUE_MISMATCH; if (thisType.checkEquivalenceHelper(thatType, eqMethod)) { newMatchType = EquivalenceMatch.VALUE_MATCH; } if (thisMatch != EquivalenceMatch.VALUE_MATCH) { thisMatch = newMatchType; } if (thatMatches[j] != EquivalenceMatch.VALUE_MATCH) { thatMatches[j] = newMatchType; } } } if (failedEquivalenceCheck(thisMatch, eqMethod)) { return false; } } for (int i = 0; i < thatMatches.length; i++) { if (failedEquivalenceCheck(thatMatches[i], eqMethod)) { return false; } } return true; } /** * Determines if the specified EquivalenceMatch is considered a failing * condition for an equivalence check, given the EquivalenceMethod used for * the check. */ private boolean failedEquivalenceCheck( EquivalenceMatch eqMatch, EquivalenceMethod eqMethod) { return eqMatch == EquivalenceMatch.VALUE_MISMATCH || (eqMatch == EquivalenceMatch.NO_KEY_MATCH && eqMethod != EquivalenceMethod.INVARIANT); } /** * Extends this TemplateTypeMap with the contents of the specified map. * UNKNOWN_TYPE will be used as the value for any missing values in the * specified map. */ TemplateTypeMap extend(TemplateTypeMap thatMap) { thatMap = thatMap.addUnknownValues(); return registry.createTemplate

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>TypeMap( concatImmutableLists(thatMap.templateKeys, templateKeys), concatImmutableLists(thatMap.templateValues, templateValues)); } /** * Returns a new TemplateTypeMap whose values have been extended with the * specified list. */ TemplateTypeMap addValues(ImmutableList<JSType> newValues) { // Ignore any new template values that will not align with an existing // template key. int numUnfilledKeys = numUnfilledTemplateKeys(); if (numUnfilledKeys < newValues.size()) { newValues = newValues.subList(0, numUnfilledKeys); } return registry.createTemplateTypeMap( templateKeys, concatImmutableLists(templateValues, newValues)); } /** * Returns a new TemplateTypeMap, where all unfilled values have been filled * with UNKNOWN_TYPE. */ private TemplateTypeMap addUnknownValues() { int numUnfilledTemplateKeys = numUnfilledTemplateKeys(); if (numUnfilledTemplateKeys == 0) { return this; } ImmutableList.Builder<JSType> builder = ImmutableList.builder(); for (int i = 0; i < numUnfilledTemplateKeys; i++) { builder.add(registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); } return addValues(builder.build()); } /** * Concatenates two ImmutableList instances. If either input is empty, the * other is returned; otherwise, a new ImmutableList instance is created that * contains the contents of both arguments. */ private <T> ImmutableList<T> concatImmutableLists( ImmutableList<T> first, ImmutableList<T> second) { if (first.isEmpty()) { return second; } if (second.isEmpty()) { return first; } ImmutableList.Builder<T> builder = ImmutableList.builder(); builder.addAll(first); builder.addAll(second); return builder.build(); } boolean hasAnyTemplateTypesInternal() { for (JSType templateValue : templateValues) { if (templateValue.hasAnyTemplateTypes()) { return true; } } return false; } @Override public String toString() { String s = ""; int len = Math.max(Math.max(templateKeys.size(), templateValues.size()), resolvedTemplateValues.size()); s += "{ "; for (int i = 0; i < len; i++) { s += "("; s += (i < templateKeys.size()) ? templateKeys.get(i) : ""; s += ","; s += (i < templateValues.size()) ? templateValues.get(i) : ""; s += ","; s += (i < resolvedTemplateValues.size()) ? resolvedTemplateValues.get(i) : ""; s += ") "; } s += "}"; return s; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; import java.util.regex.Pattern; /** * This describes the Google-specific JavaScript coding conventions. * Within Google, variable names are semantically significant. * */ public class GoogleCodingConvention extends CodingConventions.Proxy { private static final long serialVersionUID = 1L; private static final String OPTIONAL_ARG_PREFIX = "opt_"; private static final String VAR_ARGS_NAME = "var_args"; private static final Pattern ENUM_KEY_PATTERN = Pattern.compile("[A-Z0-9][A-Z0-9_]*"); /** By default, decorate the ClosureCodingConvention. */ public GoogleCodingConvention() { this(new ClosureCodingConvention()); } /** Decorates a wrapped CodingConvention. */ public GoogleCodingConvention(CodingConvention convention) { super(convention); } /** * {@inheritDoc} * * <p>This enforces the Google const name convention, that the first character * after the last $ must be an upper-case letter and all subsequent letters * must be upper case. The name must be at least 2 characters long. * * <p>Examples: * <pre> * aaa Not constant - lower-case letters in the name * A Not constant - too short * goog$A Constant - letters after the $ are upper-case. * AA17 Constant - digits can appear after the first letter * goog$7A Not constant - first character after the $ must be * upper case. * $A Constant - doesn't have to be anything in front of the $ * </pre> */ @Override public boolean isConstant(String name) { if (name.length() <= 1) { return false; } // In compiled code, '$' is often a namespace delimiter. To allow inlining // of namespaced constants, we strip off any namespaces here. int pos = name.lastIndexOf('$'); if (pos >= 0) { name = name.substring(pos + 1); if (name.length() == 0) { return false; } } return isConstantKey(name); } @Override public boolean isConstantKey(String name) { if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { return false; } // hack way of checking that there aren't any lower-case letters return name.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>toUpperCase().equals(name); } /** * {@inheritDoc} * * <p>This enforces Google's convention about enum key names. They must match * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. * * <p>Examples: * <ul> * <li>A</li> * <li>213</li> * <li>FOO_BAR</li> * </ul> */ @Override public boolean isValidEnumKey(String key) { return ENUM_KEY_PATTERN.matcher(key).matches(); } /** * {@inheritDoc} * * <p>In Google code, parameter names beginning with {@code opt_} are * treated as optional arguments. */ @Override public boolean isOptionalParameter(Node parameter) { return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); } @Override public boolean isVarArgsParameter(Node parameter) { return VAR_ARGS_NAME.equals(parameter.getString()); } /** * {@inheritDoc} * * <p>In Google code, any global name starting with an underscore is * considered exported. */ @Override public boolean isExported(String name, boolean local) { return super.isExported(name, local) || (!local && name.startsWith("_")); } /** * {@inheritDoc} * * <p>In Google code, private names end with an underscore, and exported * names are never considered private (see {@link #isExported}). */ @Override public boolean isPrivate(String name) { return name.endsWith("_") && !isExported(name); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Predicate; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.TernaryValue; /** * Checks functions for missing return statements. Return statements are only * expected for functions with return type information. Functions with empty * bodies are ignored. * */ class CheckMissingReturn implements ScopedCallback { static final DiagnosticType MISSING_RETURN_STATEMENT = DiagnosticType.warning( "JSC_MISSING_RETURN_STATEMENT", "Missing return statement. Function expected to return {0}."); private final AbstractCompiler compiler; private final CheckLevel level; private static final Predicate<Node> IS_RETURN = new Predicate<Node>() { @Override public boolean apply(Node input) { // Check for null because the control flow graph's implicit return node is // represented by null, so this value might be input. return input != null && input.isReturn(); } }; /* Skips all exception edges and impossible edges. */ private static final Predicate<DiGraphEdge<Node, ControlFlowGraph.Branch>> GOES_THROUGH_TRUE_CONDITION_PREDICATE = new Predicate<DiGraphEdge<Node, ControlFlowGraph.Branch>>() { @Override public boolean apply(DiGraphEdge<Node, ControlFlowGraph.Branch> input) { // First skill all exceptions. Branch branch = input.getValue(); if (branch == Branch.ON_EX) { return false; } else if (branch.isConditional()) { Node condition = NodeUtil.getConditionExpression( input.getSource().getValue()); // TODO(user): We CAN make this bit smarter just looking at // constants. We DO have a full blown ReverseAbstractInterupter and // type system that can evaluate some impressions' boolean value but // for now we will keep this pass lightweight. if (condition != null) { TernaryValue val = NodeUtil.getImpureBooleanValue(condition); if (val != TernaryValue.UNKNOWN) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> return val.toBoolean(true) == (Branch.ON_TRUE == branch); } } } return true; } }; /** * @param level level of severity to report when a missing return statement * is discovered */ CheckMissingReturn(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.level = level; } @Override public void enterScope(NodeTraversal t) { JSType returnType = explicitReturnExpected(t.getScopeRoot()); if (returnType == null) { return; } if (fastAllPathsReturnCheck(t.getControlFlowGraph())) { return; } CheckPathsBetweenNodes<Node, ControlFlowGraph.Branch> test = new CheckPathsBetweenNodes<Node, ControlFlowGraph.Branch>( t.getControlFlowGraph(), t.getControlFlowGraph().getEntry(), t.getControlFlowGraph().getImplicitReturn(), IS_RETURN, GOES_THROUGH_TRUE_CONDITION_PREDICATE); if (!test.allPathsSatisfyPredicate()) { compiler.report( t.makeError(t.getScopeRoot(), level, MISSING_RETURN_STATEMENT, returnType.toString())); } } /** * Fast check to see if all execution paths contain a return statement. * May spuriously report that a return statement is missing. * * @return true if all paths return, converse not necessarily true */ private static boolean fastAllPathsReturnCheck(ControlFlowGraph<Node> cfg) { for (DiGraphEdge<Node, Branch> s : cfg.getImplicitReturn().getInEdges()) { if (!s.getSource().getValue().isReturn()) { return false; } } return true; } @Override public void exitScope(NodeTraversal t) { } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { } /** * Determines if the given scope should explicitly return. All functions * with non-void or non-unknown return types must have explicit returns. * * Exception: Constructors which specifically specify a return type are * used to allow invocation without requiring the "new" keyword. They * have an implicit return type. See unit tests. * * @return If a return type is expected, returns it. Otherwise, returns null. */ private JSType explicitReturnExpected(Node scope) { FunctionType scopeType = JSType.toMaybeFunctionType(scope.getJSType()); if (scopeType == null) { return null; } if (isEmptyFunction(scope)) { return null; } if (scopeType.isConstructor()) { return null; } JSType returnType = scopeType.getReturnType(); if (returnType == null) { return null; } if (!isVoidOrUnknown(returnType)) { return returnType; } return null; } /** * @return {@code true} if function represents a JavaScript function * with an empty body */ private static boolean isEmptyFunction(Node function) { return function.getChildCount()

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; /** * Memoize a scope creator. * * This allows you to make multiple passes, without worrying about * the expense of generating Scope objects over and over again. * * On the other hand, you also have to be more aware of what your passes * are doing. Scopes are memoized stupidly, so if the underlying tree * changes, the scope may be out of sync. * * @author nicksantos@google.com (Nick Santos) */ class MemoizedScopeCreator implements ScopeCreator, StaticSymbolTable<Var, Var> { private final Map<Node, Scope> scopes = Maps.newHashMap(); private final ScopeCreator delegate; /** * @param delegate The real source of Scope objects. */ MemoizedScopeCreator(ScopeCreator delegate) { this.delegate = delegate; } @Override public Iterable<Var> getReferences(Var var) { return ImmutableList.of(var); } @Override public Scope getScope(Var var) { return var.scope; } @Override public Iterable<Var> getAllSymbols() { List<Var> vars = Lists.newArrayList(); for (Scope s : scopes.values()) { Iterables.addAll(vars, s.getAllSymbols()); } return vars; } @Override public Scope createScope(Node n, Scope parent) { Scope scope = scopes.get(n); if (scope == null) { scope = delegate.createScope(n, parent); scopes.put(n, scope); } else { Preconditions.checkState(parent == scope.getParent()); } return scope; } Collection<Scope> getAllMemoizedScopes() { return Collections.unmodifiableCollection(scopes.values()); } Scope getScopeIfMemoized(Node n) { return scopes.get(n

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.graph; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; /** * A directed graph using linked list within nodes to store edge information. * <p> * This implementation favors directed graph operations inherited from <code> * DirectedGraph</code>. * Operations from <code>Graph</code> would tends to be slower. * * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. */ public class LinkedDirectedGraph<N, E> extends DiGraph<N, E> implements GraphvizGraph { protected final Map<N, LinkedDirectedGraphNode<N, E>> nodes = Maps.newHashMap(); @Override public SubGraph<N, E> newSubGraph() { return new SimpleSubGraph<N, E>(this); } public static <N, E> LinkedDirectedGraph<N, E> createWithoutAnnotations() { return new LinkedDirectedGraph<N, E>(false, false); } public static <N, E> LinkedDirectedGraph<N, E> createWithNodeAnnotations() { return new LinkedDirectedGraph<N, E>(true, false); } public static <N, E> LinkedDirectedGraph<N, E> createWithEdgeAnnotations() { return new LinkedDirectedGraph<N, E>(false, true); } public static <N, E> LinkedDirectedGraph<N, E> create() { return new LinkedDirectedGraph<N, E>(true, true); } private final boolean useNodeAnnotations; private final boolean useEdgeAnnotations; protected LinkedDirectedGraph( boolean useNodeAnnotations, boolean useEdgeAnnotations) { this.useNodeAnnotations = useNodeAnnotations; this.useEdgeAnnotations = useEdgeAnnotations; } @Override public void connect(N srcValue, E edgeValue, N destValue) { LinkedDirectedGraphNode<N, E> src = getNodeOrFail(srcValue); LinkedDirectedGraphNode<N, E> dest = getNodeOrFail(destValue); LinkedDirectedGraphEdge<

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>N, E> edge = useEdgeAnnotations ? new AnnotatedLinkedDirectedGraphEdge<N, E>(src, edgeValue, dest) : new LinkedDirectedGraphEdge<N, E>(src, edgeValue, dest); src.getOutEdges().add(edge); dest.getInEdges().add(edge); } @Override public void disconnect(N n1, N n2) { disconnectInDirection(n1, n2); disconnectInDirection(n2, n1); } @Override public void disconnectInDirection(N srcValue, N destValue) { LinkedDirectedGraphNode<N, E> src = getNodeOrFail(srcValue); LinkedDirectedGraphNode<N, E> dest = getNodeOrFail(destValue); for (DiGraphEdge<?, E> edge : getDirectedGraphEdges(srcValue, destValue)) { src.getOutEdges().remove(edge); dest.getInEdges().remove(edge); } } @Override public Iterable<DiGraphNode<N, E>> getDirectedGraphNodes() { return Collections.<DiGraphNode<N, E>>unmodifiableCollection( nodes.values()); } @Override public DiGraphNode<N, E> getDirectedGraphNode(N nodeValue) { return nodes.get(nodeValue); } @Override public GraphNode<N, E> getNode(N nodeValue) { return getDirectedGraphNode(nodeValue); } @Override public List<DiGraphEdge<N, E>> getInEdges(N nodeValue) { LinkedDirectedGraphNode<N, E> node = getNodeOrFail(nodeValue); return Collections.<DiGraphEdge<N, E>>unmodifiableList(node.getInEdges()); } @Override public List<DiGraphEdge<N, E>> getOutEdges(N nodeValue) { LinkedDirectedGraphNode<N, E> node = getNodeOrFail(nodeValue); return Collections.<DiGraphEdge<N, E>>unmodifiableList(node.getOutEdges()); } @Override public DiGraphNode<N, E> createDirectedGraphNode(N nodeValue) { LinkedDirectedGraphNode<N, E> node = nodes.get(nodeValue); if (node == null) { node = useNodeAnnotations ? new AnnotatedLinkedDirectedGraphNode<N, E>(nodeValue) : new LinkedDirectedGraphNode<N, E>(nodeValue); nodes.put(nodeValue, node); } return node; } @Override public List<DiGraphEdge<N, E>> getEdges(N n1, N n2) { // Since this is a method from a generic graph, edges from both // directions must be added to the returning list. List<DiGraphEdge<N, E>> forwardEdges = getDirectedGraphEdges(n1, n2); List<DiGraphEdge<N, E>> backwardEdges = getDirectedGraphEdges(n2, n1); int totalSize = forwardEdges.size() + backwardEdges.size(); List<DiGraphEdge<N, E>> edges = Lists.newArrayListWithCapacity(totalSize); edges.addAll(forwardEdges); edges.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>addAll(backwardEdges); return edges; } @Override public GraphEdge<N, E> getFirstEdge(N n1, N n2) { DiGraphNode<N, E> dNode1 = getNodeOrFail(n1); DiGraphNode<N, E> dNode2 = getNodeOrFail(n2); for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { return outEdge; } } for (DiGraphEdge<N, E> outEdge : dNode2.getOutEdges()) { if (outEdge.getDestination() == dNode1) { return outEdge; } } return null; } @Override public GraphNode<N, E> createNode(N value) { return createDirectedGraphNode(value); } @Override public List<DiGraphEdge<N, E>> getDirectedGraphEdges(N n1, N n2) { DiGraphNode<N, E> dNode1 = getNodeOrFail(n1); DiGraphNode<N, E> dNode2 = getNodeOrFail(n2); List<DiGraphEdge<N, E>> edges = Lists.newArrayList(); for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { edges.add(outEdge); } } return edges; } @Override public boolean isConnectedInDirection(N n1, N n2) { return isConnectedInDirection(n1, Predicates.<E>alwaysTrue(), n2); } @Override public boolean isConnectedInDirection(N n1, E edgeValue, N n2) { return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2); } private boolean isConnectedInDirection(N n1, Predicate<E> edgeMatcher, N n2) { // Verify the nodes. DiGraphNode<N, E> dNode1 = getNodeOrFail(n1); DiGraphNode<N, E> dNode2 = getNodeOrFail(n2); for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2 && edgeMatcher.apply(outEdge.getValue())) { return true; } } return false; } @Override public List<DiGraphNode<N, E>> getDirectedPredNodes(N nodeValue) { return getDirectedPredNodes(nodes.get(nodeValue)); } @Override public List<DiGraphNode<N, E>> getDirectedSuccNodes(N nodeValue) { return getDirectedSuccNodes(nodes.get(nodeValue)); } @Override public List<DiGraphNode<N, E>> getDirectedPredNodes( DiGraphNode<N, E> dNode) { if (dNode == null) { throw new IllegalArgumentException(dNode + " is null"); } List<DiGraphNode<N, E>> nodeList = Lists.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>newArrayList(); for (DiGraphEdge<N, E> edge : dNode.getInEdges()) { nodeList.add(edge.getSource()); } return nodeList; } @Override public List<DiGraphNode<N, E>> getDirectedSuccNodes( DiGraphNode<N, E> dNode) { if (dNode == null) { throw new IllegalArgumentException(dNode + " is null"); } List<DiGraphNode<N, E>> nodeList = Lists.newArrayList(); for (DiGraphEdge<N, E> edge : dNode.getOutEdges()) { nodeList.add(edge.getDestination()); } return nodeList; } @Override public List<GraphvizEdge> getGraphvizEdges() { List<GraphvizEdge> edgeList = Lists.newArrayList(); for (LinkedDirectedGraphNode<N, E> node : nodes.values()) { for (DiGraphEdge<N, E> edge : node.getOutEdges()) { edgeList.add((LinkedDirectedGraphEdge<N, E>) edge); } } return edgeList; } @Override public List<GraphvizNode> getGraphvizNodes() { List<GraphvizNode> nodeList = Lists.newArrayListWithCapacity(nodes.size()); for (LinkedDirectedGraphNode<N, E> node : nodes.values()) { nodeList.add(node); } return nodeList; } @Override public String getName() { return "LinkedGraph"; } @Override public boolean isDirected() { return true; } @Override public Collection<DiGraphNode<N, E>> getNodes() { return Collections.<DiGraphNode<N, E>>unmodifiableCollection(nodes.values()); } @Override public List<GraphNode<N, E>> getNeighborNodes(N value) { DiGraphNode<N, E> node = getDirectedGraphNode(value); return getNeighborNodes(node); } public List<GraphNode<N, E>> getNeighborNodes(DiGraphNode<N, E> node) { List<GraphNode<N, E>> result = Lists.newArrayList(); for (Iterator<GraphNode<N, E>> i = ((LinkedDirectedGraphNode<N, E>) node).neighborIterator(); i.hasNext();) { result.add(i.next()); } return result; } @Override public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) { LinkedDirectedGraphNode<N, E> node = nodes.get(value); Preconditions.checkNotNull(node); return node.neighborIterator(); } @Override public List<DiGraphEdge<N, E>> getEdges() { List<DiGraphEdge<N, E>> result = Lists.newArrayList(); for (DiGraphNode<N, E> node : nodes.values()) { for (DiGraphEdge<N, E> edge : node.getOutEdges()) { result.add(edge); } } return Collections.unmodifiableList(result); } @Override public int getNodeDegree(N value) { DiGraphNode<N,

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> E> node = getNodeOrFail(value); return node.getInEdges().size() + node.getOutEdges().size(); } /** * A directed graph node that stores outgoing edges and incoming edges as an * list within the node itself. */ static class LinkedDirectedGraphNode<N, E> implements DiGraphNode<N, E>, GraphvizNode { List<DiGraphEdge<N, E>> inEdgeList = Lists.newArrayList(); List<DiGraphEdge<N, E>> outEdgeList = Lists.newArrayList(); protected final N value; /** * Constructor * * @param nodeValue Node's value. */ LinkedDirectedGraphNode(N nodeValue) { this.value = nodeValue; } @Override public N getValue() { return value; } @Override public <A extends Annotation> A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + hashCode(); } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String toString() { return getLabel(); } @Override public List<DiGraphEdge<N, E>> getInEdges() { return inEdgeList; } @Override public List<DiGraphEdge<N, E>> getOutEdges() { return outEdgeList; } private Iterator<GraphNode<N, E>> neighborIterator() { return new NeighborIterator(); } private class NeighborIterator implements Iterator<GraphNode<N, E>> { private final Iterator<DiGraphEdge<N, E>> in = inEdgeList.iterator(); private final Iterator<DiGraphEdge<N, E>> out = outEdgeList.iterator(); @Override public boolean hasNext() { return in.hasNext() || out.hasNext(); } @Override public GraphNode<N, E> next() { boolean isOut = !in.hasNext(); Iterator<DiGraphEdge<N, E>> curIterator = isOut ? out : in; DiGraphEdge<N, E> s = curIterator.next(); return isOut ? s.getDestination() : s.getSource(); } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported."); } } } /** * A directed graph node with annotations. */ static class AnnotatedLinkedDirectedGraphNode<N, E> extends LinkedDirectedGraphNode<N, E> { protected Annotation annotation; /** * @param nodeValue Node's value. */ AnnotatedLinkedDirectedGraphNode(N nodeValue) { super(nodeValue); } @SuppressWarnings("unchecked") @Override public <A extends Annotation> A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } /**

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * A directed graph edge that stores the source and destination nodes at each * edge. */ static class LinkedDirectedGraphEdge<N, E> implements DiGraphEdge<N, E>, GraphvizEdge { private DiGraphNode<N, E> sourceNode; private DiGraphNode<N, E> destNode; protected final E value; /** * Constructor. * * @param edgeValue Edge Value. */ LinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode, E edgeValue, DiGraphNode<N, E> destNode) { this.value = edgeValue; this.sourceNode = sourceNode; this.destNode = destNode; } @Override public DiGraphNode<N, E> getSource() { return sourceNode; } @Override public DiGraphNode<N, E> getDestination() { return destNode; } @Override public void setDestination(DiGraphNode<N, E> node) { destNode = node; } @Override public void setSource(DiGraphNode<N, E> node) { sourceNode = node; } @Override public E getValue() { return value; } @Override public <A extends Annotation> A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public String getColor() { return "black"; } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String getNode1Id() { return ((LinkedDirectedGraphNode<N, E>) sourceNode).getId(); } @Override public String getNode2Id() { return ((LinkedDirectedGraphNode<N, E>) destNode).getId(); } @Override public String toString() { return sourceNode.toString() + " -> " + destNode.toString(); } @Override public GraphNode<N, E> getNodeA() { return sourceNode; } @Override public GraphNode<N, E> getNodeB() { return destNode; } } /** * A directed graph edge that stores the source and destination nodes at each * edge. */ static class AnnotatedLinkedDirectedGraphEdge<N, E> extends LinkedDirectedGraphEdge<N, E> { protected Annotation annotation; /** * Constructor. * * @param edgeValue Edge Value. */ AnnotatedLinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode, E edgeValue, DiGraphNode<N, E> destNode) { super(sourceNode, edgeValue, destNode); } @SuppressWarnings("unchecked") @Override public <A extends Annotation> A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Prepare the AST before we do any checks or optimizations on it. * * This pass must run. It should bring the AST into a consistent state, * and add annotations where necessary. It should not make any transformations * on the tree that would lose source information, since we need that source * information for checks. * * @author johnlenz@google.com (John Lenz) */ class PrepareAst implements CompilerPass { private final AbstractCompiler compiler; private final boolean checkOnly; PrepareAst(AbstractCompiler compiler) { this(compiler, false); } PrepareAst(AbstractCompiler compiler, boolean checkOnly) { this.compiler = compiler; this.checkOnly = checkOnly; } private void reportChange() { if (checkOnly) { Preconditions.checkState(false, "normalizeNodeType constraints violated"); } } @Override public void process(Node externs, Node root) { if (checkOnly) { normalizeNodeTypes(root); } else { // Don't perform "PrepareAnnotations" when doing checks as // they currently aren't valid during sanity checks. In particular, // they DIRECT_EVAL shouldn't be applied after inlining has been // performed. if (externs != null) { NodeTraversal.traverse( compiler, externs, new PrepareAnnotations()); } if (root != null) { NodeTraversal.traverse( compiler, root, new PrepareAnnotations()); } } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && !n.isLabel() && !n.isSwitch()) { for (Node c = n.getFirstChild(); c

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n, c) && !c.isBlock()) { Node newBlock = IR.block().srcref(n); n.replaceChild(c, newBlock); if (!c.isEmpty()) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations implements NodeTraversal.Callback { PrepareAnnotations() { } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateDispatchers(n, parent); break; } } private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } } /** * There are two types of calls we are interested in calls without explicit * "this" values (what we are call "free" calls) and direct call to eval. */ private void annotateCalls(Node n) { Preconditions.checkState(n.isCall()); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = n.getFirstChild(); // ignore cast nodes. while (first.isCast()) { first = first.getFirstChild(); } if (!NodeUtil.isGet(first)) { n.putBooleanProp(Node.FREE_CALL, true); } // Keep track of the context in which eval is called. It is important // to distinguish between "(0, eval)()" and "eval()". if (first.isName() && "eval".equals(first.getString())) { first.putBooleanProp(Node.DIRECT_EVAL, true); } } /** * Translate dispatcher info into the property expected node. */ private void annotateDispatchers(Node n, Node parent) { Preconditions.checkState(n.isFunction()); if (parent.getJSDocInfo() != null && parent.getJSDocInfo().isJavaDispatch()) { if (parent.isAssign()) { Preconditions.checkState(parent.getLastChild() == n); n.putBooleanProp(Node.IS_DISPATCHER, true); } } } /** * In the AST that Rhino gives us, it needs to make a distinction * between JsDoc on the object literal node and JsDoc on the object literal * value

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ private void normalizeObjectLiteralKeyAnnotations( Node objlit, Node key, Node value) { Preconditions.checkState(objlit.isObjectLit()); if (key.getJSDocInfo() != null && value.isFunction()) { value.setJSDocInfo(key.getJSDocInfo()); } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> JSType newType) { Var newVar = var; boolean allowDupe = false; if (n.isGetProp() || NodeUtil.isObjectLitKey(n)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); newVar = s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.isEquivalentTo(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } return newVar; } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { expectInterfaceProperty(t, n, instance, implemented, prop); } } } } /** * Expect that the property in an interface that this type implements is * implemented and correctly typed. */ private void expectInterfaceProperty(NodeTraversal t, Node n, ObjectType instance, ObjectType implementedInterface, String prop) { StaticSlot<JSType> propSlot = instance.getSlot(prop); if (propSlot ==

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } private JSError report(JSError error) { if (shouldReport) { compiler.report(error); } return error; } /** * Signals that the first type and the second type have been * used interchangeably. * * Type-based optimizations should take this into account * so that they don't wreck code with type warnings. */ static class TypeMismatch { final JSType typeA; final JSType typeB; final JSError src; /** * It's the responsibility of the class that creates the * {@code TypeMismatch} to ensure that {@code a} and {@code b} are * non-matching types. */ TypeMismatch(JSType a, JSType b, JSError src) { this.typeA = a; this.typeB = b; this.src = src; } @Override public boolean equals(Object object) { if (object instanceof TypeMismatch) { TypeMismatch that = (TypeMismatch) object; return (that.typeA.isEquivalentTo(this.typeA) && that.typeB.isEquivalentTo(this.typeB)) || (that.typeB.isEquivalentTo(this.typeA) && that.typeA.isEquivalentTo(this.typeB)); } return false; } @Override public int hashCode() { return Objects.hashCode(typeA, typeB); } @Override public String toString() { return "(" + typeA + ", " + typeB + ")"; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.type; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import com.google.common.base.Function; import com.google.javascript.jscomp.CodingConvention; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSType.TypePair; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.UnionType; import com.google.javascript.rhino.jstype.Visitor; /** * A reverse abstract interpreter using the semantics of the JavaScript * language as a means to reverse interpret computations. This interpreter * expects the parse tree inputs to be typed. * */ public class SemanticReverseAbstractInterpreter extends ChainableReverseAbstractInterpreter { /** * Merging function for equality between types. */ private static final Function<TypePair, TypePair> EQ = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderEquality(p.typeB); } }; /** * Merging function for non-equality between types. */ private static final Function<TypePair, TypePair> NE = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderInequality(p.typeB); } }; /** * Merging function for strict equality between types. */ private static final Function<TypePair, TypePair> SHEQ = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null;

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } return p.typeA.getTypesUnderShallowEquality(p.typeB); } }; /** * Merging function for strict non-equality between types. */ private static final Function<TypePair, TypePair> SHNE = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderShallowInequality(p.typeB); } }; /** * Merging function for inequality comparisons between types. */ private final Function<TypePair, TypePair> ineq = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { return new TypePair( getRestrictedWithoutUndefined(p.typeA), getRestrictedWithoutUndefined(p.typeB)); } }; /** * Creates a semantic reverse abstract interpreter. */ public SemanticReverseAbstractInterpreter(CodingConvention convention, JSTypeRegistry typeRegistry) { super(convention, typeRegistry); } @Override public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { // Check for the typeof operator. int operatorToken = condition.getType(); switch (operatorToken) { case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.isTypeOf() && right.isString()) { typeOfNode = left; stringNode = right; } else if (right.isTypeOf() && left.isString()) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>IsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, left, leftType, leftIsRefineable ? merged.typeA : null, right, rightType, rightIsRefineable ? merged.typeB : null); } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; leftType = left.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); } // restricting left type JSType restrictedLeftType = (leftType == null) ? null : leftType.getRestrictedTypeGivenToBooleanOutcome(condition); if (restrictedLeftType == null) { return firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, left, leftType, leftIsRefineable ? restrictedLeftType : null, right, rightType, rightIsRefineable ? restrictedRightType : null); } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); StaticSlot<JSType> leftVar = leftScope.findUniqueRefinedSlot(blindScope); if (leftVar == null) { // If we did create a more precise scope, blindScope has a child and // it is frozen. We can't just throw it away to return it. So we // must create a child instead. return blindScope == leftScope ? blindScope : blindScope.createChildFlowScope(); } FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); rightScope = firstPreciserScopeKnowingConditionOutcome( right, rightScope, !condition); StaticSlot<JSType> rightVar = rightScope.findUniqueRefinedSlot(blindScope);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Refinable(left, blindScope); if (leftType == null) { return blindScope; } JSType rightType = right.getJSType(); ObjectType targetType = typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); if (rightType != null && rightType.isFunctionType()) { targetType = rightType.toMaybeFunctionType(); } Visitor<JSType> visitor; if (outcome) { visitor = new RestrictByTrueInstanceOfResultVisitor(targetType); } else { visitor = new RestrictByFalseInstanceOfResultVisitor(targetType); } return maybeRestrictName( blindScope, left, leftType, leftType.visit(visitor)); } /** * Given 'property in object', ensures that the object has the property in the * informed scope by defining it as a qualified name if the object type lacks * the property and it's not in the blind scope. * @param object The node of the right-side of the in. * @param propertyName The string of the left-side of the in. */ private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) { JSType jsType = object.getJSType(); jsType = this.getRestrictedWithoutNull(jsType); jsType = this.getRestrictedWithoutUndefined(jsType); boolean hasProperty = false; ObjectType objectType = ObjectType.cast(jsType); if (objectType != null) { hasProperty = objectType.hasProperty(propertyName); } if (!hasProperty) { String qualifiedName = object.getQualifiedName(); if (qualifiedName != null) { String propertyQualifiedName = qualifiedName + "." + propertyName; if (blindScope.getSlot(propertyQualifiedName) == null) { FlowScope informed = blindScope.createChildFlowScope(); JSType unknownType = typeRegistry.getNativeType( JSTypeNative.UNKNOWN_TYPE); informed.inferQualifiedSlot( object, propertyQualifiedName, unknownType, unknownType); return informed; } } } return blindScope; } /** * @see SemanticReverseAbstractInterpreter#caseInstanceOf */ private class RestrictByTrueInstanceOfResultVisitor extends RestrictByTrueTypeOfResultVisitor { private final ObjectType target; RestrictByTrueInstanceOfResultVisitor(ObjectType target) { this.target = target; } @Override protected JSType caseTopType(JSType type) { return applyCommonRestriction(type); } @Override public JSType caseUnknownType() { FunctionType funcTarget = JSType.toMaybeFunctionType(target); if (funcTarget != null && funcTarget.hasInstanceType()) { return funcTarget.getInstanceType(); } return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return applyCommonRestriction(type); } @Override public JSType caseUnionType(UnionType type) { return applyCommonRestriction(type); } @Override public JSType caseFunctionType(FunctionType type) { return caseObjectType(type); } private JSType applyCommonRestriction(JSType type) { if (target.isUnknownType()) { return type;

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } FunctionType funcTarget = target.toMaybeFunctionType(); if (funcTarget.hasInstanceType()) { return type.getGreatestSubtype(funcTarget.getInstanceType()); } return null; } } /** * @see SemanticReverseAbstractInterpreter#caseInstanceOf */ private class RestrictByFalseInstanceOfResultVisitor extends RestrictByFalseTypeOfResultVisitor { private final ObjectType target; RestrictByFalseInstanceOfResultVisitor(ObjectType target) { this.target = target; } @Override public JSType caseObjectType(ObjectType type) { if (target.isUnknownType()) { return type; } FunctionType funcTarget = target.toMaybeFunctionType(); if (funcTarget.hasInstanceType()) { if (type.isSubtype(funcTarget.getInstanceType())) { return null; } return type; } return null; } @Override public JSType caseUnionType(UnionType type) { if (target.isUnknownType()) { return type; } FunctionType funcTarget = target.toMaybeFunctionType(); if (funcTarget.hasInstanceType()) { return type.getRestrictedUnion(funcTarget.getInstanceType()); } return null; } @Override public JSType caseFunctionType(FunctionType type) { return caseObjectType(type); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.graph.LatticeElement; import java.util.List; /** * Defines a way join a list of LatticeElements. */ interface JoinOp<L extends LatticeElement> extends Function<List<L>, L> { /** * An implementation of {@code JoinOp} that makes it easy to join to * lattice elements at a time. */ abstract static class BinaryJoinOp<L extends LatticeElement> implements JoinOp<L> { @Override public final L apply(List<L> values) { Preconditions.checkArgument(!values.isEmpty()); int size = values.size(); if (size == 1) { return values.get(0); } else if (size == 2) { return apply(values.get(0), values.get(1)); } else { int mid = computeMidPoint(size); return apply( apply(values.subList(0, mid)), apply(values.subList(mid, size))); } } /** * Creates a new lattice that will be the join of two input lattices. * * @return The join of {@code latticeA} and {@code latticeB}. */ abstract L apply(L latticeA, L latticeB); /** * Finds the midpoint of a list. The function will favor two lists of * even length instead of two lists of the same odd length. The list * must be at least length two. * * @param size Size of the list. */ static int computeMidPoint(int size) { int midpoint = size >>> 1; if (size > 4) { /* Any list longer than 4 should prefer an even split point * over the true midpoint, so that [0,6] splits at 2, not 3. */ midpoint &= -2; // (0xfffffffe) clears low bit so midpoint is even } return midpoint; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ /** * For functions with function(this: T, ...) and T as arguments, type inference * will set the type of this on a function literal argument to the actual type * of T. * */ package com.google.javascript.rhino.jstype; public class TemplateType extends ProxyObjectType { private static final long serialVersionUID = 1L; private final String name; TemplateType(JSTypeRegistry registry, String name) { super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); this.name = name; } @Override public String getReferenceName() { return name; } @Override String toStringHelper(boolean forAnnotations) { return name; } @Override public TemplateType toMaybeTemplateType() { return this; } @Override public boolean hasAnyTemplateTypesInternal() { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseTemplateType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseTemplateType(this, that); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Checks for certain uses of the {@code this} keyword that are considered * unsafe because they are likely to reference the global {@code this} object * unintentionally. * * <p>A use of {@code this} is considered unsafe if it's on the left side of an * assignment or a property access, and not inside one of the following: * <ol> * <li>a prototype method * <li>a function annotated with {@code @constructor} * <li>a function annotated with {@code @this}. * <li>a function where there's no logical place to put a * {@code this} annotation. * </ol> * * <p>Note that this check does not track assignments of {@code this} to * variables or objects. The code * <pre> * function evil() { * var a = this; * a.useful = undefined; * } * </pre> * will not get flagged, even though it is semantically equivalent to * <pre> * function evil() { * this.useful = undefined; * } * </pre> * which would get flagged. * */ final class CheckGlobalThis implements Callback { static final DiagnosticType GLOBAL_THIS = DiagnosticType.warning( "JSC_USED_GLOBAL_THIS", "dangerous use of the global 'this' object"); private final AbstractCompiler compiler; /** * If {@code assignLhsChild != null}, then the node being traversed is * a descendant of the first child of an ASSIGN node. assignLhsChild's * parent is this ASSIGN node. */ private Node assignLhsChild = null; CheckGlobalThis(AbstractCompiler compiler) { this.compiler = compiler; } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { // Don't traverse functions that are constructors or have the @this // or @override annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || pType == Token.ASSIGN || // object literal keys pType == Token.STRING_KEY)) { return false; } // Don't traverse functions that are getting lent to a prototype. Node gramps = parent.getParent(); if (NodeUtil.isObjectLitKey(parent)) { JSDocInfo maybeLends = gramps.getJSDocInfo(); if (maybeLends != null && maybeLends.getLendsName() != null && maybeLends.getLendsName().endsWith(".prototype")) { return false; } } } if (parent != null && parent.isAssign()) { Node lhs = parent.getFirstChild(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { // Only traverse the right side if it's not an assignment to a prototype // property or subproperty. if (NodeUtil.isGet(lhs)) { if (lhs.isGetProp() && lhs.getLastChild().getString().equals("prototype")) { return false; } Node llhs = lhs.getFirstChild(); if (llhs.isGetProp() && llhs.getLastChild().getString().equals("prototype")) { return false; } } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isThis() && shouldReportThis(n)) { compiler.report(t.makeError(n, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n) { Node parent = n.getParent(); if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. return parent != null && NodeUtil.isGet(parent); } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} * ... x = function() {}; * var ... x = function() {}; * ... var x

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.collect.Maps; import com.google.javascript.rhino.Node; import java.util.Collections; import java.util.HashMap; /** * A builder for record types. * */ public class RecordTypeBuilder { private boolean isEmpty = true; private boolean isDeclared = true; private final JSTypeRegistry registry; private final HashMap<String, RecordProperty> properties = Maps.newHashMap(); public RecordTypeBuilder(JSTypeRegistry registry) { this.registry = registry; } /** See the comments on RecordType about synthetic types. */ void setSynthesized(boolean synthesized) { isDeclared = !synthesized; } /** * Adds a property with the given name and type to the record type. * @param name the name of the new property * @param type the JSType of the new property * @param propertyNode the node that holds this property definition * @return The builder itself for chaining purposes, or null if there's * a duplicate. */ public RecordTypeBuilder addProperty(String name, JSType type, Node propertyNode) { isEmpty = false; if (properties.containsKey(name)) { return null; } properties.put(name, new RecordProperty(type, propertyNode)); return this; } /** * Creates a record. * @

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> sourceName, line, lineOffset, type, message); } return JSError.make(sourceName, line, lineOffset, defaultLevel, PARSE_ERROR, message); } private static class OldRhinoErrorReporter extends RhinoErrorReporter implements ErrorReporter { private OldRhinoErrorReporter(AbstractCompiler compiler) { super(compiler); } @Override public void error(String message, String sourceName, int line, int lineOffset) { super.errorAtLine(message, sourceName, line, lineOffset); } @Override public void warning(String message, String sourceName, int line, int lineOffset) { super.warningAtLine(message, sourceName, line, lineOffset); } } private static class NewRhinoErrorReporter extends RhinoErrorReporter implements com.google.javascript.rhino.head.ast.IdeErrorReporter { private NewRhinoErrorReporter(AbstractCompiler compiler) { super(compiler); } @Override public com.google.javascript.rhino.head.EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) { DiagnosticType type = mapError(message); if (type != null) { super.errorAtLine(message, sourceName, line, lineOffset); } return new com.google.javascript.rhino.head.EvaluatorException( message, sourceName, line, lineSource, lineOffset); } @Override public void error(String message, String sourceName, int line, String sourceLine, int lineOffset) { super.errorAtLine(message, sourceName, line, lineOffset); } @Override public void error(String message, String sourceName, int offset, int length) { int line = 1; int column = 0; SourceFile file = this.compiler.getSourceFileByName(sourceName); if (file != null) { line = file.getLineOfOffset(offset); column = file.getColumnOfOffset(offset); } super.errorAtLine(message, sourceName, line, column); } @Override public void warning(String message, String sourceName, int line, String sourceLine, int lineOffset) { super.warningAtLine(message, sourceName, line, lineOffset); } @Override public void warning(String message, String sourceName, int offset, int length) { int line = 1; int column = 0; SourceFile file = this.compiler.getSourceFileByName(sourceName); if (file != null) { line = file.getLineOfOffset(offset); column = file.getColumnOfOffset(offset); } super.errorAtLine(message, sourceName, line, column); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import java.io.Serializable; import java.text.MessageFormat; /** * The type of a compile or analysis error. * */ public class DiagnosticType implements Comparable<DiagnosticType>, Serializable { private static final long serialVersionUID = 1; /** * The error type. Used as the BugPattern and BugInstance types by * BugBot's XML */ public final String key; /** The default way to format errors */ public final MessageFormat format; /** Default level */ public final CheckLevel defaultLevel; /** Reporting level, initially the defaultLevel but may be changed. */ public CheckLevel level; /** * Create a DiagnosticType at level CheckLevel.ERROR * * @param name An identifier * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType error(String name, String descriptionFormat) { return make(name, CheckLevel.ERROR, descriptionFormat); } /** * Create a DiagnosticType at level CheckLevel.WARNING * * @param name An identifier * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType warning(String name, String descriptionFormat) { return make(name, CheckLevel.WARNING, descriptionFormat); } /** * Create a DiagnosticType at level CheckLevel.OFF * * @param name An identifier * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType disabled(String name, String descriptionFormat) { return make(name, CheckLevel.OFF, descriptionFormat); } /** * Create a DiagnosticType at a given CheckLevel. * * @param name An identifier * @param level Either CheckLevel.ERROR or CheckLevel.WARNING * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType make(String name, CheckLevel level, String descriptionFormat) { return new DiagnosticType(name, level, new MessageFormat(descriptionFormat)); } /** * Create a DiagnosticType. Private to force use of static factory methods. */ private DiagnosticType(String key, CheckLevel level, MessageFormat format) { this.key = key; this.defaultLevel = level; this.format = format; this.level = this.defaultLevel; } /** * Create a description from the MessageFormat and the arguments. * Used by unit tests. */ String format(Object ... arguments) { return format.format(arguments);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } @Override public boolean equals(Object type) { return type instanceof DiagnosticType && ((DiagnosticType) type).key.equals(key); } @Override public int hashCode() { return key.hashCode(); } @Override public int compareTo(DiagnosticType diagnosticType) { return key.compareTo(diagnosticType.key); } @Override public String toString() { return key + ": " + format.toPattern(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Boolean type. */ public class BooleanType extends ValueType { private static final long serialVersionUID = 1L; BooleanType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNullable() { return false; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN)) || that.isObject()) { return UNKNOWN; } return FALSE; } @Override public boolean isBooleanValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>); } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "boolean"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseBooleanType(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>GLOBAL_DEFINE_INIT_ERROR", "@define variable {0} assignment must be global"); static final DiagnosticType DEFINE_NOT_ASSIGNABLE_ERROR = DiagnosticType.error( "JSC_DEFINE_NOT_ASSIGNABLE_ERROR", "@define variable {0} cannot be reassigned due to code at {1}."); private static final MessageFormat REASON_DEFINE_NOT_ASSIGNABLE = new MessageFormat("line {0} of {1}"); /** * Create a pass that overrides define constants. * * TODO(nicksantos): Write a builder to help JSCompiler induce * {@code replacements} from command-line flags * * @param replacements A hash table of names of defines to their replacements. * All replacements <b>must</b> be literals. */ ProcessDefines(AbstractCompiler compiler, Map<String, Node> replacements) { this.compiler = compiler; dominantReplacements = replacements; } /** * Injects a pre-computed global namespace, so that the same namespace * can be re-used for multiple check passes. Returns {@code this} for * easy chaining. */ ProcessDefines injectNamespace(GlobalNamespace namespace) { this.namespace = namespace; return this; } @Override public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, root); } overrideDefines(collectDefines(root, namespace)); } private void overrideDefines(Map<String, DefineInfo> allDefines) { boolean changed = false; for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) { String defineName = def.getKey(); DefineInfo info = def.getValue(); Node inputValue = dominantReplacements.get(defineName); Node finalValue = inputValue != null ? inputValue : info.getLastValue(); if (finalValue != info.initialValue) { info.initialValueParent.replaceChild( info.initialValue, finalValue.cloneTree()); compiler.addToDebugLog("Overriding @define variable " + defineName); changed = changed || finalValue.getType() != info.initialValue.getType() || !finalValue.isEquivalentTo(info.initialValue); } } if (changed) { compiler.reportCodeChange(); } Set<String> unusedReplacements = dominantReplacements.keySet(); unusedReplacements.removeAll(allDefines.keySet()); unusedReplacements.removeAll(KNOWN_DEFINES); for (String unknownDefine : unusedReplacements) { compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine)); } } private static String format(MessageFormat format, Object... params) { return format.format(params); } /** * Only defines of literal number, string, or boolean are supported. */ private boolean isValidDefineType(JSTypeExpression expression) { JSType type = expression.evaluate(null, compiler.getTypeRegistry()); return !type.isUnknownType() && type.isSubtype( compiler.getTypeRegistry().getNativeType( JSTypeNative.NUMBER_STRING_BOOLEAN)); } /** * Finds all defines

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>, and creates a {@link DefineInfo} data structure for * each one. * @return A map of {@link DefineInfo} structures, keyed by name. */ private Map<String, DefineInfo> collectDefines(Node root, GlobalNamespace namespace) { // Find all the global names with a @define annotation List<Name> allDefines = Lists.newArrayList(); for (Name name : namespace.getNameIndex().values()) { Ref decl = name.getDeclaration(); if (name.docInfo != null && name.docInfo.isDefine()) { // Process defines should not depend on check types being enabled, // so we look for the JSDoc instead of the inferred type. if (isValidDefineType(name.docInfo.getType())) { allDefines.add(name); } else { JSError error = JSError.make( decl.getSourceName(), decl.node, INVALID_DEFINE_TYPE_ERROR); compiler.report(error); } } else { for (Ref ref : name.getRefs()) { if (ref == decl) { // Declarations were handled above. continue; } Node n = ref.node; Node parent = ref.node.getParent(); JSDocInfo info = n.getJSDocInfo(); if (info == null && parent.isVar() && parent.hasOneChild()) { info = parent.getJSDocInfo(); } if (info != null && info.isDefine()) { allDefines.add(name); break; } } } } CollectDefines pass = new CollectDefines(compiler, allDefines); NodeTraversal.traverse(compiler, root, pass); return pass.getAllDefines(); } /** * Finds all assignments to @defines, and figures out the last value of * the @define. */ private static final class CollectDefines implements Callback { private final AbstractCompiler compiler; private final Map<String, DefineInfo> assignableDefines; private final Map<String, DefineInfo> allDefines; private final Map<Node, RefInfo> allRefInfo; // A hack that allows us to remove ASSIGN/VAR statements when // we're currently visiting one of the children of the assign. private Node lvalueToRemoveLater = null; // A stack tied to the node traversal, to keep track of whether // we're in a conditional block. If 1 is at the top, assignment to // a define is allowed. Otherwise, it's not allowed. private final Deque<Integer> assignAllowed; CollectDefines(AbstractCompiler compiler, List<Name> listOfDefines) { this.compiler = compiler; this.allDefines = Maps.newHashMap(); assignableDefines = Maps.newHashMap(); assignAllowed = new ArrayDeque<Integer>(); assignAllowed.push(1); // Create a map of references to defines keyed by node for easy lookup allRefInfo = Maps.newHashMap(); for (Name name : listOfDefines) { Ref decl = name.getDeclaration(); if (decl != null) { allRefInfo.put(decl.node, new RefInfo(decl, name)); } for (Ref ref : name.getRefs()) { if (ref == decl) { //

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Declarations were handled above. continue; } // If there's a TWIN def, only put one of the twins in. if (ref.getTwin() == null || !ref.getTwin().isSet()) { allRefInfo.put(ref.node, new RefInfo(ref, name)); } } } } /** * Get a map of {@link DefineInfo} structures, keyed by the name of * the define. */ Map<String, DefineInfo> getAllDefines() { return allDefines; } /** * Keeps track of whether the traversal is in a conditional branch. * We traverse all nodes of the parse tree. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { updateAssignAllowedStack(n, true); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { RefInfo refInfo = allRefInfo.get(n); if (refInfo != null) { Ref ref = refInfo.ref; Name name = refInfo.name; String fullName = name.getFullName(); switch (ref.type) { case SET_FROM_GLOBAL: case SET_FROM_LOCAL: Node valParent = getValueParent(ref); Node val = valParent.getLastChild(); if (valParent.isAssign() && name.isSimpleName() && name.getDeclaration() == ref) { // For defines, it's an error if a simple name is assigned // before it's declared compiler.report( t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName)); } else if (processDefineAssignment(t, fullName, val, valParent)) { // remove the assignment so that the variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.isAssign()) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * traversed, they will be visited by * {@link #visit(NodeTraversal, Node, Node)} in postorder.</p> * <p>Implementations can have side effects (e.g. modifying the parse * tree).</p> * @return whether the children of this node should be visited */ boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent); /** * <p>Visits a node in postorder (after its children have been visited). * A node is visited only if all its parents should be traversed * ({@link #shouldTraverse(NodeTraversal, Node, Node)}).</p> * <p>Implementations can have side effects (e.g. modifying the parse * tree).</p> */ void visit(NodeTraversal t, Node n, Node parent); } /** * Callback that also knows about scope changes */ public interface ScopedCallback extends Callback { /** * Called immediately after entering a new scope. The new scope can * be accessed through t.getScope() */ void enterScope(NodeTraversal t); /** * Called immediately before exiting a scope. The ending scope can * be accessed through t.getScope() */ void exitScope(NodeTraversal t); } /** * Abstract callback to visit all nodes in postorder. */ public abstract static class AbstractPostOrderCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } } /** Abstract callback to visit all nodes in preorder. */ public abstract static class AbstractPreOrderCallback implements Callback { @Override public void visit(NodeTraversal t, Node n, Node parent) {} } /** * Abstract scoped callback to visit all nodes in postorder. */ public abstract static class AbstractScopedCallback implements ScopedCallback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void enterScope(NodeTraversal t) {} @Override public void exitScope(NodeTraversal t) {} } /** * Abstract callback to visit all nodes but not traverse into function * bodies. */ public abstract static class AbstractShallowCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || !parent.isFunction() || n == parent.getFirstChild(); } } /** * Abstract callback to visit all structure and statement nodes but doesn't * traverse into functions or expressions. */ public abstract static class AbstractShallowStatementCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent); } } /** * Abstract callback to visit a pruned set of nodes. */ public abstract static class AbstractNodeTypePruningCallback implements Callback { private final Set<Integer> nodeTypes; private final boolean

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> include; /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) { this(nodeTypes, true); } /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include/exclude in the traversal * @param include whether to include or exclude the nodes in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes, boolean include) { this.nodeTypes = nodeTypes; this.include = include; } @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb; if (cb instanceof ScopedCallback) { this.scopeCallback = (ScopedCallback) cb; } this.compiler = compiler; this.inputId = null; this.sourceName = ""; this.scopeCreator = scopeCreator; } private void throwUnexpectedException(Exception unexpectedException) { // If there's an unexpected exception, try to get the // line number of the code that caused it. String message = unexpectedException.getMessage(); // TODO(user): It is possible to get more information if curNode or // its parent is missing. We still have the scope stack in which it is still // very useful to find out at least which function caused the exception. if (inputId != null) { message = unexpectedException.getMessage() + "\n" + formatNodeContext("Node", curNode) + (curNode == null ? "" : formatNodeContext("Parent", curNode.getParent())); } compiler.throwInternalError(message, unexpectedException); } private String formatNodeContext(String label, Node n) { if (n == null) { return " " + label + ": NULL"; } return " " + label + "(" + n.toString(false, false, false) + "): " + formatNodePosition(n); } /** * Traverses a parse tree recursively. */ public void traverse(Node root) { try { inputId = NodeUtil.getInputId(root); sourceName = ""; curNode = root; pushScope(root); // null parent ensures that the shallow callbacks will traverse root traverseBranch(root, null); popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } public void traverseRoots(Node ... roots) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List<Node> roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent();

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>); } } public AbstractCompiler getCompiler() { return compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >= 0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(inputId); } /** * Gets the current input module. */ public JSModule getModule() { CompilerInput input = getInput(); return input == null ? null : input.getModule(); } /** Returns the node currently being traversed. */ public Node getCurrentNode() { return curNode; } /** * Traversal for passes that work only on changed functions. * Suppose a loopable pass P1 uses this traversal. * Then, if a function doesn't change between two runs of P1, it won't look at * the function the second time. * (We're assuming that P1 runs to a fixpoint, o/w we may miss optimizations.) * * Most changes are reported with calls to Compiler.reportCodeChange(), which * doesn't know which scope changed. We keep track of the current scope by * calling Compiler.setScope inside pushScope and popScope. * The automatic tracking can be wrong in rare cases when a pass changes scope * w/out causing a call to pushScope or popScope. It's very hard to find the * places where this happens unless a bug is triggered. * Passes that do cross-scope modifications call * Compiler.reportChangeToEnclosingScope(Node n). */ public static void traverseChangedFunctions( AbstractCompiler compiler, FunctionCallback callback) { final AbstractCompiler comp = compiler; final FunctionCallback cb = callback; final Node jsRoot = comp.getJsRoot(); NodeTraversal t = new NodeTraversal(comp, new AbstractPreOrderCallback() { @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node p) { if ((n == jsRoot || n.isFunction()) && comp.hasScopeChanged(n)) { cb.visit(comp, n); } return true; } }); t.traverse(jsRoot); } /** * Traverses a node recursively. */ public static void traverse( AbstractCompiler compiler, Node root, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverse(root); } /** * Traverses a list of node trees. */ public static void traverseRoots( AbstractCompiler compiler, List<Node> roots, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } public static void traverseRoots( AbstractCompiler

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> = false; defineReplacements = Maps.newHashMap(); tweakProcessing = TweakProcessing.OFF; tweakReplacements = Maps.newHashMap(); moveFunctionDeclarations = false; appNameStr = ""; recordFunctionInformation = false; generateExports = false; exportLocalPropertyDefinitions = false; cssRenamingMap = null; cssRenamingWhitelist = null; processObjectPropertyString = false; idGenerators = ImmutableMap.of(); replaceStringsFunctionDescriptions = Collections.emptyList(); replaceStringsPlaceholderToken = ""; replaceStringsReservedStrings = Collections.emptySet(); propertyInvalidationErrors = Maps.newHashMap(); // Instrumentation instrumentationTemplate = null; // instrument functions instrumentMemoryAllocations = false; // instrument allocations instrumentForCoverage = false; // instrument lines // Output printInputDelimiter = false; prettyPrint = false; lineBreak = false; preferLineBreakAtEndOfFile = false; reportPath = null; tracer = TracerMode.OFF; colorizeErrorOutput = false; errorFormat = ErrorFormat.SINGLELINE; debugFunctionSideEffectsPath = null; externExports = false; nameReferenceReportPath = null; nameReferenceGraphPath = null; // Debugging aliasHandler = NULL_ALIAS_TRANSFORMATION_HANDLER; errorHandler = null; } /** * @return Whether to attempt to remove unused class properties */ public boolean isRemoveUnusedClassProperties() { return removeUnusedClassProperties; } /** * @param removeUnusedClassProperties Whether to attempt to remove * unused class properties */ public void setRemoveUnusedClassProperties(boolean removeUnusedClassProperties) { this.removeUnusedClassProperties = removeUnusedClassProperties; } /** * Returns the map of define replacements. */ public Map<String, Node> getDefineReplacements() { return getReplacementsHelper(defineReplacements); } /** * Returns the map of tweak replacements. */ public Map<String, Node> getTweakReplacements() { return getReplacementsHelper(tweakReplacements); } /** * Creates a map of String->Node from a map of String->Number/String/Boolean. */ private static Map<String, Node> getReplacementsHelper( Map<String, Object> source) { Map<String, Node> map = Maps.newHashMap(); for (Map.Entry<String, Object> entry : source.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); if (value instanceof Boolean) { map.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue())); } else if (value instanceof Integer) { map.put(name, IR.number(((Integer) value).intValue())); } else if (value instanceof Double) { map.put(name, IR.number(((Double) value).doubleValue())); } else { Preconditions.checkState(value instanceof String); map.put(name, IR.string((String) value)); } } return map; } /** * Sets the value of the {@code @define} variable in JS * to a boolean literal. */ public void setDefineToBooleanLiteral(String defineName, boolean value) { define

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>; } public LanguageMode getLanguageIn() { return languageIn; } public LanguageMode getLanguageOut() { return languageOut; } /** * Whether to include "undefined" in the default types. * For example: * "{Object}" is normally "Object|null" becomes "Object|null|undefined" * "{?string}" is normally "string|null" becomes "string|null|undefined" * In either case "!" annotated types excluded both null and undefined. */ public void setLooseTypes(boolean looseTypes) { this.looseTypes = looseTypes; } @Override public Object clone() throws CloneNotSupportedException { CompilerOptions clone = (CompilerOptions) super.clone(); // TODO(bolinfest): Add relevant custom cloning. return clone; } public void setAliasTransformationHandler( AliasTransformationHandler changes) { this.aliasHandler = changes; } public AliasTransformationHandler getAliasTransformationHandler() { return this.aliasHandler; } /** * Set a custom handler for warnings and errors. * * This is mostly used for piping the warnings and errors to * a file behind the scenes. * * If you want to filter warnings and errors, you should use a WarningsGuard. * * If you want to change how warnings and errors are reported to the user, * you should set a ErrorManager on the Compiler. An ErrorManager is * intended to summarize the errors for a single compile job. */ public void setErrorHandler(ErrorHandler handler) { this.errorHandler = handler; } /** * If true, enables type inference. If checkTypes is enabled, this flag has * no effect. */ public void setInferTypes(boolean enable) { inferTypes = enable; } /** * Gets the inferTypes flag. Note that if checkTypes is enabled, this flag * is ignored when configuring the compiler. */ public boolean getInferTypes() { return inferTypes; } /** * @return Whether assumeStrictThis is set. */ public boolean assumeStrictThis() { return assumeStrictThis; } /** * If true, enables enables additional optimizations. */ public void setAssumeStrictThis(boolean enable) { this.assumeStrictThis = enable; } /** * @return Whether assumeClosuresOnlyCaptureReferences is set. */ public boolean assumeClosuresOnlyCaptureReferences() { return assumeClosuresOnlyCaptureReferences; } /** * Whether to assume closures capture only what they reference. This allows * more aggressive function inlining. */ public void setAssumeClosuresOnlyCaptureReferences(boolean enable) { this.assumeClosuresOnlyCaptureReferences = enable; } /** * Sets the list of properties that we report property invalidation errors * for. */ public void setPropertyInvalidationErrors( Map<String, CheckLevel> propertyInvalidationErrors) { this.propertyInvalidationErrors = Maps.newHashMap(propertyInvalidationErrors); } public void setLanguageOut(LanguageMode languageOut) { this.languageOut = languageOut; } public void setIdeMode(boolean ideMode) { this.ide

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> by only 1 per file, and * the only top-level structure in the file, this is not enforced. */ public interface AliasTransformation { /** * Adds an alias definition to the AliasTransformation instance. * <p> * Last definition for a given alias is kept if an alias is inserted * multiple times (since this is generally the behavior in JavaScript code). * * @param alias the name of the alias. * @param definition the definition of the alias. */ void addAlias(String alias, String definition); } /** * A Null implementation of the CodeChanges interface which performs all * operations as a No-Op */ static final AliasTransformationHandler NULL_ALIAS_TRANSFORMATION_HANDLER = new NullAliasTransformationHandler(); private static class NullAliasTransformationHandler implements AliasTransformationHandler, Serializable { private static final long serialVersionUID = 0L; private static final AliasTransformation NULL_ALIAS_TRANSFORMATION = new NullAliasTransformation(); @Override public AliasTransformation logAliasTransformation( String sourceFile, SourcePosition<AliasTransformation> position) { position.setItem(NULL_ALIAS_TRANSFORMATION); return NULL_ALIAS_TRANSFORMATION; } private static class NullAliasTransformation implements AliasTransformation, Serializable { private static final long serialVersionUID = 0L; @Override public void addAlias(String alias, String definition) { } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * String type. */ public final class StringType extends ValueType { private static final long serialVersionUID = 1L; StringType(JSTypeRegistry registry) { super(registry); } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; } @Override public boolean isStringValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "string"; } @Override public JSType autoboxes

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>To() { return getNativeType(JSTypeNative.STRING_OBJECT_TYPE); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseStringType(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>otes an optional {@code @param}. */ public boolean isOptionalArg() { return root.getType() == Token.EQUALS; } /** * @return Whether this expression denotes a rest args {@code @param}. */ public boolean isVarArgs() { return root.getType() == Token.ELLIPSIS; } /** * Evaluates the type expression into a {@code JSType} object. */ public JSType evaluate(StaticScope<JSType> scope, JSTypeRegistry registry) { JSType type = registry.createFromTypeNodes(root, sourceName, scope); root.setJSType(type); return type; } @Override public boolean equals(Object other) { return other instanceof JSTypeExpression && ((JSTypeExpression) other).root.isEquivalentTo(root); } @Override public int hashCode() { return root.toStringTree().hashCode(); } /** * @return The source for this type expression. Note that it will not * contain an expression if there's an @override tag. */ public Node getRoot() { return root; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.io.Serializable; import java.util.Map; import java.util.Set; /** * Representation for a collection of properties on an object. * @author nicksantos@google.com (Nick Santos) */ class PropertyMap implements Serializable { private static final long serialVersionUID = 1L; private static final PropertyMap EMPTY_MAP = new PropertyMap( ImmutableMap.<String, Property>of()); private static final Function<ObjectType, PropertyMap> PROP_MAP_FROM_TYPE = new Function<ObjectType, PropertyMap>() { @Override public PropertyMap apply(ObjectType t) { return t.getPropertyMap(); } }; // A place to get the inheritance structure. // Because the extended interfaces are resolved dynamically, this gets // messy :(. If type-resolution was more well-defined, we could // just reference primary parents and secondary parents directly. private ObjectType parentSource = null; // The map of our own properties. private final Map<String, Property> properties; PropertyMap() { this

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>(Maps.<String, Property>newTreeMap()); } private PropertyMap(Map<String, Property> underlyingMap) { this.properties = underlyingMap; } static PropertyMap immutableEmptyMap() { return EMPTY_MAP; } void setParentSource(ObjectType ownerType) { if (this != EMPTY_MAP) { this.parentSource = ownerType; } } /** Returns the direct parent of this property map. */ PropertyMap getPrimaryParent() { if (parentSource == null) { return null; } ObjectType iProto = parentSource.getImplicitPrototype(); return iProto == null ? null : iProto.getPropertyMap(); } /** * Returns the secondary parents of this property map, for interfaces that * need multiple inheritance. */ Iterable<PropertyMap> getSecondaryParents() { if (parentSource == null) { return ImmutableList.of(); } Iterable<ObjectType> extendedInterfaces = parentSource.getCtorExtendedInterfaces(); // Most of the time, this will be empty. if (Iterables.isEmpty(extendedInterfaces)) { return ImmutableList.of(); } return Iterables.transform(extendedInterfaces, PROP_MAP_FROM_TYPE); } Property getSlot(String name) { if (properties.containsKey(name)) { return properties.get(name); } PropertyMap primaryParent = getPrimaryParent(); if (primaryParent != null) { Property prop = primaryParent.getSlot(name); if (prop != null) { return prop; } } for (PropertyMap p : getSecondaryParents()) { if (p != null) { Property prop = p.getSlot(name); if (prop != null) { return prop; } } } return null; } Property getOwnProperty(String propertyName) { return properties.get(propertyName); } int getPropertiesCount() { PropertyMap primaryParent = getPrimaryParent(); if (primaryParent == null) { return this.properties.size(); } Set<String> props = Sets.newHashSet(); collectPropertyNames(props); return props.size(); } boolean hasOwnProperty(String propertyName) { return properties.get(propertyName) != null; } boolean hasProperty(String propertyName) { return getSlot(propertyName) != null; } Set<String> getOwnPropertyNames() { return properties.keySet(); } void collectPropertyNames(Set<String> props) { for (String prop : properties.keySet()) { props.add(prop); } PropertyMap primaryParent = getPrimaryParent(); if (primaryParent != null) { primaryParent.collectPropertyNames(props); } for (PropertyMap p : getSecondaryParents()) { if (p != null) { p.collectPropertyNames(props); } } } boolean removeProperty(String name) { return properties.remove(name) != null; } void putProperty(String name, Property newProp) { Property oldProp = properties.get(name); if (oldProp != null) { // This is to keep previously inferred JsDoc info, e.g., in a // replaceScript scenario. newProp

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>code> if the edge exists. */ public abstract boolean isConnectedInDirection(N n1, E edgeValue, N n2); @Override public boolean isConnected(N n1, N n2) { return isConnectedInDirection(n1, n2) || isConnectedInDirection(n2, n1); } @Override public boolean isConnected(N n1, E e, N n2) { return isConnectedInDirection(n1, e, n2) || isConnectedInDirection(n2, e, n1); } /** * A generic directed graph node. * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. */ public static interface DiGraphNode<N, E> extends GraphNode<N, E> { public List<DiGraphEdge<N, E>> getOutEdges(); public List<DiGraphEdge<N, E>> getInEdges(); } /** * A generic directed graph edge. * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. */ public static interface DiGraphEdge<N, E> extends GraphEdge<N, E> { public DiGraphNode<N, E> getSource(); public DiGraphNode<N, E> getDestination(); public void setSource(DiGraphNode<N, E> node); public void setDestination(DiGraphNode<N, E> node); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; import com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.rhino.Node; import java.util.Map; /** * A compiler pass to run the type inference analysis. * */ class TypeInferencePass implements CompilerPass { static final DiagnosticType DATAFLOW_ERROR = DiagnosticType.warning( "JSC_INTERNAL_ERROR_DATAFLOW", "non-monotonic data-flow analysis"); private final AbstractCompiler compiler; private final ReverseAbstractInterpreter reverseInterpreter; private final Scope topScope; private final MemoizedScopeCreator scopeCreator; private final Map<String, AssertionFunctionSpec> assertionFunctionsMap; TypeInferencePass(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, Scope topScope, MemoizedScopeCreator scopeCreator) { this.compiler = compiler; this.reverseInterpreter = reverseInterpreter; this.topScope = topScope; this.scopeCreator = scopeCreator; assertionFunctionsMap = Maps.newHashMap(); for (AssertionFunctionSpec assertionFunction : compiler.getCodingConvention().getAssertionFunctions()) { assertionFunctionsMap.put(assertionFunction.getFunctionName(), assertionFunction); } } /** * Main entry point for type inference when running over the whole tree. * * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ @Override public void process(Node externsRoot, Node jsRoot) { Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); inferAllScopes(externsAndJs); } /** Entry point for type inference when running over part of the tree. */ void inferAllScopes(Node node) { // Type analysis happens in two major phases. // 1) Finding all the symbols. // 2) Propagating all the inferred types. // // The order of this analysis is non-obvious. In a complete inference // system, we may need to backtrack arbitrarily far. But the compile-time // costs would be unaccept

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>able. // // We do one pass where we do typed scope creation for all scopes // in pre-order. // // Then we do a second pass where we do all type inference // (type propagation) in pre-order. // // We use a memoized scope creator so that we never create a scope // more than once. // // This will allow us to handle cases like: // var ns = {}; // (function() { /** JSDoc */ ns.method = function() {}; })(); // ns.method(); // In this code, we need to build the symbol table for the inner scope in // order to propagate the type of ns.method in the outer scope. (new NodeTraversal( compiler, new FirstScopeBuildingCallback(), scopeCreator)) .traverseWithScope(node, topScope); for (Scope s : scopeCreator.getAllMemoizedScopes()) { s.resolveTypes(); } (new NodeTraversal( compiler, new SecondScopeBuildingCallback(), scopeCreator)) .traverseWithScope(node, topScope); } void inferScope(Node n, Scope scope) { TypeInference typeInference = new TypeInference( compiler, computeCfg(n), reverseInterpreter, scope, assertionFunctionsMap); try { typeInference.analyze(); // Resolve any new type names found during the inference. compiler.getTypeRegistry().resolveTypesInScope(scope); } catch (DataFlowAnalysis.MaxIterationsExceededException e) { compiler.report(JSError.make(n.getSourceFileName(), n, DATAFLOW_ERROR)); } } private class FirstScopeBuildingCallback extends AbstractScopedCallback { @Override public void enterScope(NodeTraversal t) { t.getScope(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Do nothing } } private class SecondScopeBuildingCallback extends AbstractScopedCallback { @Override public void enterScope(NodeTraversal t) { // Only infer the entry root, rather than the scope root. // This ensures that incremental compilation only touches the root // that's been swapped out. inferScope(t.getCurrentNode(), t.getScope()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Do nothing } } private ControlFlowGraph<Node> computeCfg(Node n) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); cfa.process(null, n); return cfa.getCfg(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.io.Serializable; import java.util.*; import java.util.Map; import java.util.TreeSet; /** * WarningsGuard that represents just a chain of other guards. For example we * could have following chain * 1) all warnings outside of /foo/ should be suppressed * 2) errors with key JSC_BAR should be marked as warning * 3) the rest should be reported as error * * This class is designed for such behavior. * * @author anatol@google.com (Anatol Pomazau) */ public class ComposeWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; // The order that the guards were added in. private final Map<WarningsGuard, Integer> orderOfAddition = Maps.newHashMap(); private int numberOfAdds = 0; private final Comparator<WarningsGuard> guardComparator = new GuardComparator(orderOfAddition); private boolean demoteErrors = false; private static class GuardComparator implements Comparator<WarningsGuard>, Serializable { private static final long serialVersionUID = 1L; private final Map<WarningsGuard, Integer> orderOfAddition; private GuardComparator(Map<WarningsGuard, Integer> orderOfAddition) { this.orderOfAddition = orderOfAddition; } @Override public int compare(WarningsGuard a, WarningsGuard b) { int priorityDiff = a.getPriority() - b.getPriority(); if (priorityDiff != 0) { return priorityDiff; } // If the warnings guards have the same priority, the one that // was added last wins. return orderOfAddition.get(b).intValue() - orderOfAddition.get(a).intValue(); } } // The order that the guards are applied in. private final TreeSet<WarningsGuard> guards = new TreeSet<WarningsGuard>(guardComparator); public ComposeWarningsGuard(List<WarningsGuard> guards) { addGuards(guards); } public ComposeWarningsGuard(WarningsGuard... guards) { this(Lists.newArrayList(guards)); } void addGuard(WarningsGuard guard) { if (guard instanceof ComposeWarningsGuard) { ComposeWarningsGuard composeGuard = (ComposeWarningsGuard) guard; if (composeGuard.demoteErrors) { this

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.demoteErrors = composeGuard.demoteErrors; } // Reverse the guards, so that they have the same order in the result. addGuards(Lists.newArrayList(composeGuard.guards.descendingSet())); } else { numberOfAdds++; orderOfAddition.put(guard, numberOfAdds); guards.remove(guard); guards.add(guard); } } private void addGuards(Iterable<WarningsGuard> guards) { for (WarningsGuard guard : guards) { addGuard(guard); } } @Override public CheckLevel level(JSError error) { for (WarningsGuard guard : guards) { CheckLevel newLevel = guard.level(error); if (newLevel != null) { if (demoteErrors && newLevel == CheckLevel.ERROR) { return CheckLevel.WARNING; } return newLevel; } } return null; } @Override public boolean disables(DiagnosticGroup group) { nextSingleton: for (DiagnosticType type : group.getTypes()) { DiagnosticGroup singleton = DiagnosticGroup.forType(type); for (WarningsGuard guard : guards) { if (guard.disables(singleton)) { continue nextSingleton; } else if (guard.enables(singleton)) { return false; } } return false; } return true; } /** * Determines whether this guard will "elevate" the status of any disabled * diagnostic type in the group to a warning or an error. */ @Override public boolean enables(DiagnosticGroup group) { for (WarningsGuard guard : guards) { if (guard.enables(group)) { return true; } else if (guard.disables(group)) { return false; } } return false; } List<WarningsGuard> getGuards() { return Collections.unmodifiableList(Lists.newArrayList(guards)); } /** * Make a warnings guard that's the same as this one but with * all escalating guards turned down. */ ComposeWarningsGuard makeEmergencyFailSafeGuard() { ComposeWarningsGuard safeGuard = new ComposeWarningsGuard(); safeGuard.demoteErrors = true; for (WarningsGuard guard : guards.descendingSet()) { safeGuard.addGuard(guard); } return safeGuard; } @Override public String toString() { return Joiner.on(", ").join(guards); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.SortedSet; /** * <p>A basic error manager that sorts all errors and warnings reported to it to * generate a sorted report when the {@link #generateReport()} method * is called.</p> * * <p>This error manager does not produce any output, but subclasses can * override the {@link #println(CheckLevel, JSError)} method to generate custom * output.</p> * */ public abstract class BasicErrorManager implements ErrorManager { private final SortedSet<ErrorWithLevel> messages = Sets.newTreeSet(new LeveledJSErrorComparator()); private int errorCount = 0; private int warningCount = 0; private double typedPercent = 0.0; @Override public void report(CheckLevel level, JSError error) { if (messages.add(new ErrorWithLevel(error, level))) { if (level == CheckLevel.ERROR) { errorCount++; } else if (level == CheckLevel.WARNING) { warningCount++; } } } @Override public void generateReport() { for (ErrorWithLevel message : messages) { println(message.level, message.error); } printSummary(); } /** * Print a message with a trailing new line. This method is called by the * {@link #generateReport()} method when generating messages. */ public abstract void println(CheckLevel level, JSError error); /** * Print the summary of the compilation - number of errors and warnings. */ protected abstract void printSummary(); @Override public int getErrorCount() { return errorCount; } @Override public int getWarningCount() { return warningCount; } @Override public JSError[] getErrors() { return toArray(CheckLevel.ERROR); } @Override public JSError[] getWarnings() { return toArray(CheckLevel.WARNING); } @Override public void setTypedPercent(double typedPercent) { this.typedPercent = typedPercent; } @Override public double getTypedPercent() { return typedPercent; } private JSError[] toArray(CheckLevel level) { List<JSError> errors = new ArrayList<JSError>(messages.size()); for (ErrorWithLevel p : messages) { if (p.level == level) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> errors.add(p.error); } } return errors.toArray(new JSError[errors.size()]); } /** * <p>Comparator of {@link JSError} with an associated {@link CheckLevel}. * The ordering is the standard lexical ordering on the quintuple * (file name, line number, {@link CheckLevel}, * character number, description).</p> * * <p>Note: this comparator imposes orderings that are inconsistent with * {@link JSError#equals(Object)}.</p> */ static final class LeveledJSErrorComparator implements Comparator<ErrorWithLevel> { private static final int P1_LT_P2 = -1; private static final int P1_GT_P2 = 1; @Override public int compare(ErrorWithLevel p1, ErrorWithLevel p2) { // null is the smallest value if (p2 == null) { if (p1 == null) { return 0; } else { return P1_GT_P2; } } // check level if (p1.level != p2.level) { return p2.level.compareTo(p1.level); } // sourceName comparison String source1 = p1.error.sourceName; String source2 = p2.error.sourceName; if (source1 != null && source2 != null) { int sourceCompare = source1.compareTo(source2); if (sourceCompare != 0) { return sourceCompare; } } else if (source1 == null && source2 != null) { return P1_LT_P2; } else if (source1 != null && source2 == null) { return P1_GT_P2; } // lineno comparison int lineno1 = p1.error.lineNumber; int lineno2 = p2.error.lineNumber; if (lineno1 != lineno2) { return lineno1 - lineno2; } else if (lineno1 < 0 && 0 <= lineno2) { return P1_LT_P2; } else if (0 <= lineno1 && lineno2 < 0) { return P1_GT_P2; } // charno comparison int charno1 = p1.error.getCharno(); int charno2 = p2.error.getCharno(); if (charno1 != charno2) { return charno1 - charno2; } else if (charno1 < 0 && 0 <= charno2) { return P1_LT_P2; } else if (0 <= charno1 && charno2 < 0) { return P1_GT_P2; } // description return p1.error.description.compareTo(p2.error.description); } } static class ErrorWithLevel { final JSError error; final CheckLevel level; ErrorWithLevel(JSError error, CheckLevel level) { this.error = error; this.level = level; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2013 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; /** * Records whether the code has changed since the last reset. * @author nicksantos@google.com (Nick Santos) */ final class RecentChange implements CodeChangeHandler { private boolean hasChanged = false; @Override public void reportChange() { hasChanged = true; } public boolean hasCodeChanged() { return hasChanged; } public void reset() { hasChanged = false; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>FunctionScope() { return cache.functionScope; } /** Whether this flows from a bottom scope. */ private boolean flowsFromBottom() { return getFunctionScope().isBottom(); } /** * Creates an entry lattice for the flow. */ public static LinkedFlowScope createEntryLattice(Scope scope) { return new LinkedFlowScope(new FlatFlowScopeCache(scope)); } @Override public void inferSlotType(String symbol, JSType type) { Preconditions.checkState(!frozen); lastSlot = new LinkedFlowSlot(symbol, type, lastSlot); depth++; cache.dirtySymbols.add(symbol); } @Override public void inferQualifiedSlot(Node node, String symbol, JSType bottomType, JSType inferredType) { Scope functionScope = getFunctionScope(); if (functionScope.isLocal()) { if (functionScope.getVar(symbol) == null && !functionScope.isBottom()) { functionScope.declare(symbol, node, bottomType, null); } inferSlotType(symbol, inferredType); } } @Override public JSType getTypeOfThis() { return cache.functionScope.getTypeOfThis(); } @Override public Node getRootNode() { return getFunctionScope().getRootNode(); } @Override public StaticScope<JSType> getParentScope() { return getFunctionScope().getParentScope(); } /** * Get the slot for the given symbol. */ @Override public StaticSlot<JSType> getSlot(String name) { if (cache.dirtySymbols.contains(name)) { for (LinkedFlowSlot slot = lastSlot; slot != null; slot = slot.parent) { if (slot.getName().equals(name)) { return slot; } } } return cache.getSlot(name); } @Override public StaticSlot<JSType> getOwnSlot(String name) { throw new UnsupportedOperationException(); } @Override public FlowScope createChildFlowScope() { frozen = true; if (depth > MAX_DEPTH) { if (flattened == null) { flattened = new FlatFlowScopeCache(this); } return new LinkedFlowScope(flattened); } return new LinkedFlowScope(this); } /** * Iterate through all the linked flow scopes before this one. * If there's one and only one slot defined between this scope * and the blind scope, return it. */ @Override public StaticSlot<JSType> findUniqueRefinedSlot(FlowScope blindScope) { StaticSlot<JSType> result = null; for (LinkedFlowScope currentScope = this; currentScope != blindScope; currentScope = currentScope.parent) { for (LinkedFlowSlot currentSlot = currentScope.lastSlot; currentSlot != null && (currentScope.parent == null || currentScope.parent.lastSlot != currentSlot); currentSlot = currentSlot.parent) { if (result == null) { result = currentSlot; } else if (!currentSlot.getName().equals(result.getName())) { return null; } } } return result; } /** * Look through the

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> given scope, and try to find slots where it doesn't * have enough type information. Then fill in that type information * with stuff that we've inferred in the local flow. */ @Override public void completeScope(StaticScope<JSType> staticScope) { Scope scope = (Scope) staticScope; for (Iterator<Var> it = scope.getVars(); it.hasNext();) { Var var = it.next(); if (var.isTypeInferred()) { JSType type = var.getType(); if (type == null || type.isUnknownType()) { JSType flowType = getSlot(var.getName()).getType(); var.setType(flowType); } } } } /** * Remove flow scopes that add nothing to the flow. */ // NOTE(nicksantos): This function breaks findUniqueRefinedSlot, because // findUniqueRefinedSlot assumes that this scope is a direct descendant // of blindScope. This is not necessarily true if this scope has been // optimize()d and blindScope has not. This should be fixed. For now, // we only use optimize() where we know that we won't have to do // a findUniqueRefinedSlot on it. @Override public LinkedFlowScope optimize() { LinkedFlowScope current; for (current = this; current.parent != null && current.lastSlot == current.parent.lastSlot; current = current.parent) {} return current; } /** Join the two FlowScopes. */ static class FlowScopeJoinOp extends JoinOp.BinaryJoinOp<FlowScope> { @SuppressWarnings("unchecked") @Override public FlowScope apply(FlowScope a, FlowScope b) { // To join the two scopes, we have to LinkedFlowScope linkedA = (LinkedFlowScope) a; LinkedFlowScope linkedB = (LinkedFlowScope) b; linkedA.frozen = true; linkedB.frozen = true; if (linkedA.optimize() == linkedB.optimize()) { return linkedA.createChildFlowScope(); } return new LinkedFlowScope(new FlatFlowScopeCache(linkedA, linkedB)); } } @Override public boolean equals(Object other) { if (other instanceof LinkedFlowScope) { LinkedFlowScope that = (LinkedFlowScope) other; if (this.optimize() == that.optimize()) { return true; } // If two flow scopes are in the same function, then they could have // two possible function scopes: the real one and the BOTTOM scope. // If they have different function scopes, we *should* iterate through all // the variables in each scope and compare. However, 99.9% of the time, // they're not equal. And the other .1% of the time, we can pretend // they're equal--this just means that data flow analysis will have // to propagate the entry lattice a little bit further than it // really needs to. Everything will still come out ok. if (this.getFunctionScope() != that.getFunctionScope()) { return false; } if (cache == that.cache) { // If the two flow scopes have the same cache, then we can check // equality

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> a lot faster: by just looking at the "dirty" elements // in the cache, and comparing them in both scopes. for (String name : cache.dirtySymbols) { if (diffSlots(getSlot(name), that.getSlot(name))) { return false; } } return true; } Map<String, StaticSlot<JSType>> myFlowSlots = allFlowSlots(); Map<String, StaticSlot<JSType>> otherFlowSlots = that.allFlowSlots(); for (StaticSlot<JSType> slot : myFlowSlots.values()) { if (diffSlots(slot, otherFlowSlots.get(slot.getName()))) { return false; } otherFlowSlots.remove(slot.getName()); } for (StaticSlot<JSType> slot : otherFlowSlots.values()) { if (diffSlots(slot, myFlowSlots.get(slot.getName()))) { return false; } } return true; } return false; } /** * Determines whether two slots are meaningfully different for the * purposes of data flow analysis. */ private boolean diffSlots(StaticSlot<JSType> slotA, StaticSlot<JSType> slotB) { boolean aIsNull = slotA == null || slotA.getType() == null; boolean bIsNull = slotB == null || slotB.getType() == null; if (aIsNull && bIsNull) { return false; } else if (aIsNull ^ bIsNull) { return true; } // Both slots and types must be non-null. return slotA.getType().differsFrom(slotB.getType()); } /** * Gets all the symbols that have been defined before this point * in the current flow. Does not return slots that have not changed during * the flow. * * For example, consider the code: * <code> * var x = 3; * function f() { * var y = 5; * y = 6; // FLOW POINT * var z = y; * return z; * } * </code> * A FlowScope at FLOW POINT will return a slot for y, but not * a slot for x or z. */ private Map<String, StaticSlot<JSType>> allFlowSlots() { Map<String, StaticSlot<JSType>> slots = Maps.newHashMap(); for (LinkedFlowSlot slot = lastSlot; slot != null; slot = slot.parent) { if (!slots.containsKey(slot.getName())) { slots.put(slot.getName(), slot); } } for (Map.Entry<String, StaticSlot<JSType>> symbolEntry : cache.symbols.entrySet()) { if (!slots.containsKey(symbolEntry.getKey())) { slots.put(symbolEntry.getKey(), symbolEntry.getValue()); } } return slots; } @Override public int hashCode() { throw new UnsupportedOperationException(); } /** * A static slot that can be used in a linked list. */ private static class LinkedFlowSlot extends SimpleSlot { final LinkedFlowSlot parent; LinkedFlowSlot(String name, JSType type, LinkedFlowSlot parent) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> super(name, type, true); this.parent = parent; } } /** * A map that tries to cache as much symbol table information * as possible in a map. Optimized for fast lookup. */ private static class FlatFlowScopeCache { // The Scope for the entire function or for the global scope. private final Scope functionScope; // The linked flow scope that this cache represents. private final LinkedFlowScope linkedEquivalent; // All the symbols defined before this point in the local flow. // May not include lazily declared qualified names. private Map<String, StaticSlot<JSType>> symbols = Maps.newHashMap(); // Used to help make lookup faster for LinkedFlowScopes by recording // symbols that may be redefined "soon", for an arbitrary definition // of "soon". ;) // // More rigorously, if a symbol is redefined in a LinkedFlowScope, // and this is the closest FlatFlowScopeCache, then that symbol is marked // "dirty". In this way, we don't waste time looking in the LinkedFlowScope // list for symbols that aren't defined anywhere nearby. final Set<String> dirtySymbols = Sets.newHashSet(); // The cache at the bottom of the lattice. FlatFlowScopeCache(Scope functionScope) { this.functionScope = functionScope; symbols = ImmutableMap.of(); linkedEquivalent = null; } // A cache in the middle of a long scope chain. FlatFlowScopeCache(LinkedFlowScope directParent) { FlatFlowScopeCache cache = directParent.cache; functionScope = cache.functionScope; symbols = directParent.allFlowSlots(); linkedEquivalent = directParent; } // A cache at the join of two scope chains. FlatFlowScopeCache(LinkedFlowScope joinedScopeA, LinkedFlowScope joinedScopeB) { linkedEquivalent = null; // Always prefer the "real" function scope to the faked-out // bottom scope. functionScope = joinedScopeA.flowsFromBottom() ? joinedScopeB.getFunctionScope() : joinedScopeA.getFunctionScope(); Map<String, StaticSlot<JSType>> slotsA = joinedScopeA.allFlowSlots(); Map<String, StaticSlot<JSType>> slotsB = joinedScopeB.allFlowSlots(); symbols = slotsA; // There are 5 different join cases: // 1) The type is declared in joinedScopeA, not in joinedScopeB, // and not in functionScope. Just use the one in A. // 2) The type is declared in joinedScopeB, not in joinedScopeA, // and not in functionScope. Just use the one in B. // 3) The type is declared in functionScope and joinedScopeA, but // not in joinedScopeB. Join the two types. // 4) The type is declared in functionScope and joinedScopeB, but // not in joinedScopeA. Join the two types. // 5) The type is declared in joinedScopeA and joinedScopeB. Join // the two types. Set<String> symbolNames = Sets.newHashSet(symbols.keySet()); symbolNames.addAll(slotsB.keySet()); for (String name : symbolNames

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; /** * The {@code Unknown} type. */ public class UnknownType extends ObjectType { private static final long serialVersionUID = 1L; // See the explanation of checked unknown types in JSTypeNative. private final boolean isChecked; UnknownType(JSTypeRegistry registry, boolean isChecked) { super(registry); this.isChecked = isChecked; } @Override public boolean isUnknownType() { return true; } @Override public boolean isCheckedUnknownType() { return isChecked; } @Override public boolean canBeCalled() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override public boolean isNullable() { return true; } @Override public boolean isSubtype(JSType that) { return true; } @Override public <T> T visit(Visitor<T> visitor)

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> { return visitor.caseUnknownType(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseUnknownType(this, that); } @Override String toStringHelper(boolean forAnnotations) { return getReferenceName(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing to define return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public FunctionType getConstructor() { return null; } @Override public String getReferenceName() { return isChecked ? "??" : "?"; } @Override public String getDisplayName() { return "Unknown"; } @Override public boolean hasDisplayName() { return true; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Predicate; import com.google.common.collect.Maps; import com.google.debugging.sourcemap.FilePosition; import com.google.debugging.sourcemap.SourceMapFormat; import com.google.debugging.sourcemap.SourceMapGenerator; import com.google.debugging.sourcemap.SourceMapGeneratorFactory; import com.google.debugging.sourcemap.SourceMapGeneratorV1; import com.google.debugging.sourcemap.SourceMapGeneratorV2; import com.google.javascript.rhino.Node; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; /** * Collects information mapping the generated (compiled) source back to * its original source for debugging purposes. * * @see CodeConsumer * @see CodeGenerator * @see CodePrinter * */ public class SourceMap { public static enum Format { V1 { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.V1)); } }, DEFAULT { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.DEFAULT)); } }, V2 { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.V2)); } }, V3 { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3)); } }; abstract SourceMap getInstance(); } /** * Source maps can be very large different levels of detail can be specified. */ public static enum DetailLevel implements Predicate<Node> { // ALL is best when the fullest details are needed for debugging or for // code-origin analysis. ALL { @Override public boolean apply(Node node) { return true; } }, // SYMBOLS is intended to be used for stack trace deobfuscation when full // detail is not needed. SYMBOLS { @Override public boolean apply(Node node) { return node.isCall() || node.isNew() || node.isFunction() || node.isName() || NodeUtil.isGet(node) || NodeUtil.isObjectLitKey(node) || (node.isString() && NodeUtil.isGet(node.getParent())); } }; } public

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> static class LocationMapping { final String prefix; final String replacement; public LocationMapping(String prefix, String replacement) { this.prefix = prefix; this.replacement = replacement; } } private final SourceMapGenerator generator; private List<LocationMapping> prefixMappings = Collections.emptyList(); private final Map<String, String> sourceLocationFixupCache = Maps.newHashMap(); private SourceMap(SourceMapGenerator generator) { this.generator = generator; } public void addMapping( Node node, FilePosition outputStartPosition, FilePosition outputEndPosition) { String sourceFile = node.getSourceFileName(); // If the node does not have an associated source file or // its line number is -1, then the node does not have sufficient // information for a mapping to be useful. if (sourceFile == null || node.getLineno() < 0) { return; } sourceFile = fixupSourceLocation(sourceFile); String originalName = (String) node.getProp(Node.ORIGINALNAME_PROP); // Strangely, Rhino source lines are one based but columns are // zero based. // We don't change this for the v1 or v2 source maps but for // v3 we make them both 0 based. int lineBaseOffset = 1; if (generator instanceof SourceMapGeneratorV1 || generator instanceof SourceMapGeneratorV2) { lineBaseOffset = 0; } generator.addMapping( sourceFile, originalName, new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()), outputStartPosition, outputEndPosition); } /** * @param sourceFile The source file location to fixup. * @return a remapped source file. */ private String fixupSourceLocation(String sourceFile) { if (prefixMappings.isEmpty()) { return sourceFile; } String fixed = sourceLocationFixupCache.get(sourceFile); if (fixed != null) { return fixed; } // Replace the first prefix found with its replacement for (LocationMapping mapping : prefixMappings) { if (sourceFile.startsWith(mapping.prefix)) { fixed = mapping.replacement + sourceFile.substring( mapping.prefix.length()); break; } } // If none of the mappings match then use the original file path. if (fixed == null) { fixed = sourceFile; } sourceLocationFixupCache.put(sourceFile, fixed); return fixed; } public void appendTo(Appendable out, String name) throws IOException { generator.appendTo(out, name); } public void reset() { generator.reset(); sourceLocationFixupCache.clear(); } public void setStartingPosition(int offsetLine, int offsetIndex) { generator.setStartingPosition(offsetLine, offsetIndex); } public void setWrapperPrefix(String prefix) { generator.setWrapperPrefix(prefix); } public void validate(boolean validate) { generator.validate(validate); } /** * @param sourceMapLocationMappings */ public void setPrefixMappings(List<LocationMapping> sourceMapLocationMappings) { this.prefix

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.deps.SortedDependencies; import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; import com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * A {@link JSModule} dependency graph that assigns a depth to each module and * can answer depth-related queries about them. For the purposes of this class, * a module's depth is defined as the number of hops in the longest path from * the module to a module with no dependencies. * */ public class JSModuleGraph { private List<JSModule> modules; /** * Lists of modules at each depth. <code>modulesByDepth.get(3)</code> is a * list of the modules at depth 3, for example. */ private List<List<JSModule>> modulesByDepth; /** * dependencyMap is a cache of dependencies that makes the dependsOn * function faster. Each map entry associates a starting * JSModule with the set of JSModules that are transitively dependent on the * starting module. * * If the cache returns null, then the entry hasn't been filled in for that * module. * * dependencyMap should be filled from leaf to root so that * getTransitiveDepsDeepestFirst can use its results directly. */ private Map<JSModule, Set<JSModule>> dependencyMap = Maps.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>newHashMap(); /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(JSModule[] modulesInDepOrder) { this(ImmutableList.copyOf(modulesInDepOrder)); } /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(List<JSModule> modulesInDepOrder) { Preconditions.checkState( modulesInDepOrder.size() == Sets.newHashSet(modulesInDepOrder).size(), "Found duplicate modules"); modules = ImmutableList.copyOf(modulesInDepOrder); modulesByDepth = Lists.newArrayList(); for (JSModule module : modulesInDepOrder) { int depth = 0; for (JSModule dep : module.getDependencies()) { int depDepth = dep.getDepth(); if (depDepth < 0) { throw new ModuleDependenceException(String.format( "Modules not in dependency order: %s preceded %s", module.getName(), dep.getName()), module, dep); } depth = Math.max(depth, depDepth + 1); } module.setDepth(depth); if (depth == modulesByDepth.size()) { modulesByDepth.add(new ArrayList<JSModule>()); } modulesByDepth.get(depth).add(module); } } /** * Gets an iterable over all modules in dependency order. */ Iterable<JSModule> getAllModules() { return modules; } /** * Gets all modules indexed by name. */ Map<String, JSModule> getModulesByName() { Map<String, JSModule> result = Maps.newHashMap(); for (JSModule m : modules) { result.put(m.getName(), m); } return result; } /** * Gets the total number of modules. */ int getModuleCount() { return modules.size(); } /** * Gets the root module. */ JSModule getRootModule() { return Iterables.getOnlyElement(modulesByDepth.get(0)); } /** * Returns a JSON representation of the JSModuleGraph. Specifically a * JSONArray of "Modules" where each module has a * - "name" * - "dependencies" (list of module names) * - "transitive-dependencies" (list of module names, deepest first) * - "inputs" (list of file names) * @return List of module JSONObjects. */ JSONArray toJson() { JSONArray modules = new JSONArray(); for (JSModule module : getAllModules()) { JSONObject node = new JSONObject(); try { node.put("name", module.getName()); JSONArray deps = new JSONArray(); node.put("dependencies", deps); for (JSModule m : module.getDependencies()) { deps.put(m.getName()); } JSONArray transitiveDeps = new JSONArray(); node.put("transitive-dependencies", transitiveDeps); for (JSModule m : getTransitiveDepsDeepestFirst(module)) { transitiveDeps.put(m.getName()); } JSONArray inputs = new JSONArray(); node.put("inputs", inputs); for (CompilerInput input : module

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.getInputs()) { inputs.put(input.getSourceFile().getOriginalPath()); } modules.put(node); } catch (JSONException e) { Throwables.propagate(e); } } return modules; } /** * Determines whether this module depends on a given module. Note that a * module never depends on itself, as that dependency would be cyclic. */ public boolean dependsOn(JSModule src, JSModule m) { Set<JSModule> deps = dependencyMap.get(src); if (deps == null) { deps = getTransitiveDepsDeepestFirst(src); dependencyMap.put(src, deps); } return deps.contains(m); } /** * Finds the deepest common dependency of two modules, not including the two * modules themselves. * * @param m1 A module in this graph * @param m2 A module in this graph * @return The deepest common dep of {@code m1} and {@code m2}, or null if * they have no common dependencies */ JSModule getDeepestCommonDependency(JSModule m1, JSModule m2) { int m1Depth = m1.getDepth(); int m2Depth = m2.getDepth(); // According our definition of depth, the result must have a strictly // smaller depth than either m1 or m2. for (int depth = Math.min(m1Depth, m2Depth) - 1; depth >= 0; depth--) { List<JSModule> modulesAtDepth = modulesByDepth.get(depth); // Look at the modules at this depth in reverse order, so that we use the // original ordering of the modules to break ties (later meaning deeper). for (int i = modulesAtDepth.size() - 1; i >= 0; i--) { JSModule m = modulesAtDepth.get(i); if (dependsOn(m1, m) && dependsOn(m2, m)) { return m; } } } return null; } /** * Finds the deepest common dependency of two modules, including the * modules themselves. * * @param m1 A module in this graph * @param m2 A module in this graph * @return The deepest common dep of {@code m1} and {@code m2}, or null if * they have no common dependencies */ public JSModule getDeepestCommonDependencyInclusive( JSModule m1, JSModule m2) { if (m2 == m1 || dependsOn(m2, m1)) { return m1; } else if (dependsOn(m1, m2)) { return m2; } return getDeepestCommonDependency(m1, m2); } /** Returns the deepest common dependency of the given modules. */ public JSModule getDeepestCommonDependencyInclusive( Collection<JSModule> modules) { Iterator<JSModule> iter = modules.iterator(); JSModule dep = iter.next(); while (iter.hasNext()) { dep = getDeepestCommonDependencyInclusive(dep, iter.next()); } return dep

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>; } /** * Creates an iterable over the transitive dependencies of module {@code m} * in a non-increasing depth ordering. The result does not include the module * {@code m}. * * @param m A module in this graph * @return The transitive dependencies of module {@code m} */ Set<JSModule> getTransitiveDepsDeepestFirst(JSModule m) { Set<JSModule> deps = dependencyMap.get(m); if (deps != null) { return deps; } deps = new TreeSet<JSModule>(new InverseDepthComparator()); addDeps(deps, m); dependencyMap.put(m, deps); return deps; } /** * Adds a module's transitive dependencies to a set. */ private void addDeps(Set<JSModule> deps, JSModule m) { for (JSModule dep : m.getDependencies()) { deps.add(dep); addDeps(deps, dep); } } /** * Replaces any files that are found multiple times with a single instance in * the closest parent module that is common to all modules where it appears. * * JSCompiler normally errors if you attempt to compile modules containing the * same file. This method can be used to remove duplicates before compiling * to avoid such an error. */ public void coalesceDuplicateFiles() { Multimap<String, JSModule> fileRefs = LinkedHashMultimap.create(); for (JSModule module : modules) { for (CompilerInput jsFile : module.getInputs()) { fileRefs.put(jsFile.getName(), module); } } for (String path : fileRefs.keySet()) { Collection<JSModule> refModules = fileRefs.get(path); if (refModules.size() > 1) { JSModule depModule = getDeepestCommonDependencyInclusive(refModules); CompilerInput file = refModules.iterator().next().getByName(path); for (JSModule module : refModules) { if (module != depModule) { module.removeByName(path); } } if (!refModules.contains(depModule)) { depModule.add(file); } } } } /** * Applies a DependencyOptions in "dependency sorting" and "dependency pruning" * mode to the given list of inputs. Returns a new list with the files sorted * and removed. This module graph will be updated to reflect the new list. * * If you need more fine-grained dependency management, you should create your * own DependencyOptions and call * {@code manageDependencies(DependencyOptions, List<CompilerInput>)}. * * @param entryPoints The entry points into the program. * Expressed as JS symbols. * @param inputs The original list of sources. Used to ensure that the sort * is stable. * @throws CircularDependencyException if there is a circular dependency * between the provides and requires. * @throws MissingProvideException if an entry point was not provided * by any of the inputs. * @see DependencyOptions for more info on how this works. */ public List<CompilerInput> manageDependencies( List<String> entry

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> createEntryPointInputs( DependencyOptions depOptions, List<CompilerInput> inputs, SortedDependencies<CompilerInput> sorter) throws MissingModuleException, MissingProvideException { Set<CompilerInput> entryPointInputs = Sets.newLinkedHashSet(); Map<String, JSModule> modulesByName = getModulesByName(); if (depOptions.shouldPruneDependencies()) { if (!depOptions.shouldDropMoochers()) { entryPointInputs.addAll(sorter.getInputsWithoutProvides()); } for (String entryPoint : depOptions.getEntryPoints()) { // An entry point is either formatted as: // 'foo.bar' - peg foo.bar to its current module // 'modC:foo.bar' - peg foo.bar to modC String inputName = entryPoint; int splitPoint = entryPoint.indexOf(':'); CompilerInput entryPointInput = null; if (splitPoint != -1) { String moduleName = entryPoint.substring(0, splitPoint); inputName = entryPoint.substring( Math.min(splitPoint + 1, entryPoint.length() - 1)); JSModule module = modulesByName.get(moduleName); if (module == null) { throw new MissingModuleException(moduleName); } else { entryPointInput = sorter.getInputProviding(inputName); entryPointInput.overrideModule(module); } } else { entryPointInput = sorter.getInputProviding(inputName); } entryPointInputs.add(entryPointInput); } CompilerInput baseJs = sorter.maybeGetInputProviding("goog"); if (baseJs != null) { entryPointInputs.add(baseJs); } } else { entryPointInputs.addAll(inputs); } return entryPointInputs; } LinkedDirectedGraph<JSModule, String> toGraphvizGraph() { LinkedDirectedGraph<JSModule, String> graphViz = LinkedDirectedGraph.create(); for (JSModule module : getAllModules()) { graphViz.createNode(module); for (JSModule dep : module.getDependencies()) { graphViz.createNode(dep); graphViz.connect(module, "->", dep); } } return graphViz; } /** * A module depth comparator that considers a deeper module to be "less than" * a shallower module. Uses module names to consistently break ties. */ private class InverseDepthComparator implements Comparator<JSModule> { @Override public int compare(JSModule m1, JSModule m2) { return depthCompare(m2, m1); } } private int depthCompare(JSModule m1, JSModule m2) { if (m1 == m2) { return 0; } int d1 = m1.getDepth(); int d2 = m2.getDepth(); return d1 < d2 ? -1 : d2 == d1 ? m1.getName().compareTo(m2.getName()) : 1; } /** * Exception class for declaring when the modules being fed into a * JSModuleGraph as input aren't in dependence order, and so can't be * processed for caching of various dependency-

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> in {1}"); static final DiagnosticType VAR_ARGUMENTS_SHADOWED_ERROR = DiagnosticType.error( "JSC_VAR_ARGUMENTS_SHADOWED_ERROR", "Shadowing \"arguments\" is not allowed"); // The arguments variable is special, in that it's declared in every local // scope, but not explicitly declared. private static final String ARGUMENTS = "arguments"; // Vars that still need to be declared in externs. These will be declared // at the end of the pass, or when we see the equivalent var declared // in the normal code. private final Set<String> varsToDeclareInExterns = Sets.newHashSet(); private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; // Whether extern checks emit error. private final boolean strictExternCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.strictExternCheck = compiler.getErrorLevel( JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR; this.sanityCheck = sanityCheck; } /** * Create a SyntacticScopeCreator. If not in sanity check mode, use a * {@link RedeclarationCheckHandler} to check var redeclarations. * @return the SyntacticScopeCreator */ private ScopeCreator createScopeCreator() { if (sanityCheck) { return new SyntacticScopeCreator(compiler); } else { return new SyntacticScopeCreator( compiler, new RedeclarationCheckHandler()); } } @Override public void process(Node externs, Node root) { ScopeCreator scopeCreator = createScopeCreator(); // Don't run externs-checking in sanity check mode. Normalization will // remove duplicate VAR declarations, which will make // externs look like they have assigns. if (!sanityCheck) { NodeTraversal traversal = new NodeTraversal( compiler, new NameRefInExternsCheck(), scopeCreator); traversal.traverse(externs); } NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); t.traverseRoots(Lists.newArrayList(externs, root)); for (String varName : varsToDeclareInExterns) { createSynthesizedExternVar(varName); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Preconditions.checkState(scriptRoot.isScript()); ScopeCreator scopeCreator = createScopeCreator(); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); // Note we use the global scope to prevent wrong "undefined-var errors" on // variables that are defined in other JS files. Scope topScope = scopeCreator.createScope(compiler.getRoot(), null); t.traverseWithScope(scriptRoot, topScope); // TODO(bashir) Check if we need to createSynthesizedExternVar like process. } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String varName = n.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(parent.isFunction()); Preconditions.checkState(NodeUtil.isFunctionExpression(parent)); return; } // Check if this is a declaration for a var that has been declared // elsewhere. If so, mark it as a duplicate. if ((parent.isVar() || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); n.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { boolean isArguments = scope.isLocal() && ARGUMENTS.equals(varName); // The extern checks are stricter, don't report a second error. if (!isArguments && !(strictExternCheck && t.getInput().isExtern())) { t.report(n, UNDEFINED_VAR_ERROR, varName); } if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { createSynthesizedExternVar(varName); scope.getGlobalScope().declare(varName, n, null, compiler.getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (!sanityCheck && varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * Create a new variable in a synthetic script. This will prevent * subsequent compiler passes from crashing. */ private void createSynthesizedExtern

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Var(String varName) { Node nameNode = IR.name(varName); // Mark the variable as constant if it matches the coding convention // for constant vars. // NOTE(nicksantos): honestly, I'm not sure how much this matters. // AFAIK, all people who use the CONST coding convention also // compile with undeclaredVars as errors. We have some test // cases for this configuration though, and it makes them happier. if (compiler.getCodingConvention().isConstant(varName)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } getSynthesizedExternsRoot().addChildToBack( IR.var(nameNode)); varsToDeclareInExterns.remove(varName); compiler.reportCodeChange(); } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.PARAM_LIST: // These are okay. break; case Token.GETPROP: if (n == parent.getFirstChild()) { Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString()); varsToDeclareInExterns.add(n.getString()); } } break; default: t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { varsToDeclareInExterns.add(n.getString()); } break; } } } } /** * @param n The name node to check. * @param origVar The associated Var. * @return Whether duplicated declarations warnings should be suppressed * for the given node. */ static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); Node origParent = origVar.getParentNode(); JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } if (info != null && info.getSuppressions().contains("duplicate")) { return true; } info = origVar.nameNode.getJSDocInfo(); if (info == null) { info = origParent.getJSDocInfo(); } return (info != null && info.getSuppressions().contains("duplicate")); } /** * The handler for duplicate declarations. */ private class RedeclarationCheckHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n,

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> checkingPolicy; /* * Eventize DAG represented using adjacency lists. */ private Map<String, Set<String>> eventizes; /* * Maps from eventful object name to state. */ private static Map<String, EventfulObjectState> eventfulObjectMap; public CheckEventfulObjectDisposal(AbstractCompiler compiler, DisposalCheckingPolicy checkingPolicy) { this.compiler = compiler; this.checkingPolicy = checkingPolicy; this.initializeDisposeMethodsMap(); this.typeRegistry = compiler.getTypeRegistry(); } /** * Add a new call that is used to dispose an JS object. * @param functionOrMethodName The name or suffix of a function or method * that disposes of/registers an object as disposable * @param argumentsThatAreDisposed An array of integers (ideally sorted) that * specifies the arguments of the function being disposed */ private void addDisposeCall(String functionOrMethodName, List<Integer> argumentsThatAreDisposed) { String potentiallyTypeName, propertyName; JSType objectType = null; int lastPeriod = functionOrMethodName.lastIndexOf('.'); // If function call has a period it is potentially a method function. if (lastPeriod >= 0) { potentiallyTypeName = functionOrMethodName.substring(0, lastPeriod). replaceFirst(".prototype$", ""); propertyName = functionOrMethodName.substring(lastPeriod); objectType = compiler.getTypeRegistry().getType(potentiallyTypeName); } else { propertyName = functionOrMethodName; } // Find or create property map for object type Map<String, List<Integer>> map = this.disposeCalls.get(objectType); if (map == null) { map = Maps.newHashMap(); this.disposeCalls.put(objectType, map); } /* * If this is a static function call store the full function name, * else only the method of the object. */ if (objectType == null) { map.put(functionOrMethodName, argumentsThatAreDisposed); } else { map.put(propertyName, argumentsThatAreDisposed); } } /* * Initialize disposeMethods map with calls to dispose calls. */ private void initializeDisposeMethodsMap() { this.disposeCalls = Maps.newHashMap(); /* * Initialize dispose calls map. Checks for: * - Y.registerDisposable(X) * (Y has to be of type goog.Disposable) * - X.dispose() * - goog.dispose(X) * - goog.disposeAll(X...) * - X.removeAll() (X is of type goog.events.EventHandler) * - Y.add(X...) or Y.push(X) */ this.addDisposeCall("goog.dispose", new ArrayList<Integer>(Arrays.asList(0))); this.addDisposeCall("goog.Disposable.registerDisposable", new ArrayList<Integer>(Arrays.asList(0))); this.addDisposeCall("goog.disposeAll", new ArrayList<Integer>(Arrays.asList(DISPOSE_ALL))); this.addDisposeCall("goog.events.EventHandler.removeAll", new ArrayList<Integer>(Arrays.asList(DISPOSE_SELF))); this.addDisposeCall(".dispose", new ArrayList

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS><Integer>(Arrays.asList(DISPOSE_SELF))); this.addDisposeCall(".push", new ArrayList<Integer>(Arrays.asList(0))); this.addDisposeCall(".add", new ArrayList<Integer>(Arrays.asList(DISPOSE_ALL))); } private static Node getBase(Node n) { Node base = n; while (base.isGetProp()) { base = base.getFirstChild(); } return base; } /* * Get the type of the this in the current scope of traversal */ private JSType getTypeOfThisForScope(NodeTraversal t) { JSType typeOfThis = t.getScopeRoot().getJSType(); if (typeOfThis == null) { return null; } ObjectType objectType = ObjectType.cast(dereference(typeOfThis)); return objectType.getTypeOfThis(); } /** * Determines if thisType is possibly a subtype of thatType. * * It differs from isSubtype only in that thisType gets expanded * if it is a union. * * Common case targeted is a function returning an eventful object * that may also return a null. * * @param thisType the JSType being tested * @param thatType the JSType that is possibly a base of thisType * @return whether thisType is possibly subtype of thatType */ private static boolean isPossiblySubtype(JSType thisType, JSType thatType) { if (thisType == null) { return false; } JSType type = thisType; if (type.isUnionType()) { for (JSType alternate : type.toMaybeUnionType().getAlternates()) { if (alternate.isSubtype(thatType)) { return true; } } } else { if (type.isSubtype(thatType)) { return true; } } return false; } private static JSType dereference(JSType type) { return type == null ? null : type.dereference(); } /* * Create a unique identification string for Node n, or null if function * called with invalid argument. * * This function is basically used to distinguish between: * A.B = function() { * this.eh = new ... * } * and * C.D = function() { * this.eh = new ... * } * * As well as * A.B = function() { * var eh = new ... * } * and * C.D = function() { * var eh = new ... * } * * Warning: Inheritance is not currently handled. */ private static String generateKey(NodeTraversal t, Node n, boolean noLocalVariables) { if (n == null) { return null; } String key; Node scopeNode = t.getScopeRoot(); if (n.isName()) { if (noLocalVariables) { return null; } key = n.getQualifiedName(); if (scopeNode.isFunction()) { JSType parentScopeType = t.getScope().getParentScope().getTypeOfThis(); /*

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * If the locally defined variable is defined within a function, use * the function name to create ID. */ if (!parentScopeType.isGlobalThisType()) { key = parentScopeType.toString() + "~" + key; } key = NodeUtil.getFunctionName(scopeNode) + "=" + key; } } else { /* * Only handle cases such as a.b.c.X and not cases where the * eventful object is stored in an array or uses a function to * determine the index. * * Note: Inheritance changes the name that should be returned here */ if (!n.isQualifiedName()) { return null; } key = n.getQualifiedName(); /* * If it is not a simple variable and doesn't use this, then we assume * global variable. */ Node base = getBase(n); if (base != null && base.isThis()) { if (base.getJSType().isUnknownType()) { // Handle anonymous function created in constructor: // // /** // * @extends {goog.SubDisposable} // * @constructor */ // speel.Person = function() { // this.run = function() { // this.eh = new goog.events.EventHandler(); // } //}; key = t.getScope().getParentScope().getTypeOfThis().toString() + "~" + key; } else { if (n.getFirstChild() == null) { key = base.getJSType().toString() + "=" + key; } else { ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); if (objectType == null) { return null; } ObjectType hObjT = objectType; String propertyName = n.getLastChild().getString(); while (objectType != null) { hObjT = objectType; objectType = objectType.getImplicitPrototype(); if (objectType == null) { break; } if (objectType.getDisplayName().endsWith("prototype")) { continue; } if (!objectType.getPropertyNames().contains(propertyName)) { break; } } key = hObjT.toString() + "=" + key; } } } } return key; } @Override public void process(Node externs, Node root) { // This pass should not have gotten added in this case Preconditions.checkArgument(checkingPolicy != DisposalCheckingPolicy.OFF); // Initialize types googDisposableInterfaceType = compiler.getTypeRegistry().getType(DISPOSABLE_INTERFACE_TYPE_NAME); googEventsEventHandlerType = compiler.getTypeRegistry() .getType(EVENT_HANDLER_TYPE_NAME); /* * Required types not found therefore the kind of pattern considered * will not be found. */ if (googEventsEventHandlerType == null || googDisposableInterfaceType == null) { return; } // Seed list of disposable stype eventfulTypes = new HashSet<JSType>(); eventfulTypes.add(googEventsEventHandlerType); // Construct eventizer graph if (checkingPolicy == DisposalCheckingPolicy.AGGRESSIVE) { NodeTraversal.traverse(compiler, root

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>, new ComputeEventizeTraversal()); computeEventful(); } /* * eventfulObjectMap maps a eventful object's "name" to its corresponding * EventfulObjectState which tracks the state (allocated, disposed of) * as well as allocation site. */ eventfulObjectMap = new HashMap<String, EventfulObjectState>(); // Traverse tree NodeTraversal.traverse(compiler, root, new Traversal()); /* * Scan eventfulObjectMap for allocated eventful objects that * had no dispose calls. */ for (EventfulObjectState e : eventfulObjectMap.values()) { Node n = e.allocationSite; if (e.seen == SeenType.ALLOCATED) { compiler.report(JSError.make(n.getSourceFileName(), n, EVENTFUL_OBJECT_NOT_DISPOSED)); } else if (e.seen == SeenType.ALLOCATED_LOCALLY && checkingPolicy == DisposalCheckingPolicy.AGGRESSIVE) { compiler.report(JSError.make(n.getSourceFileName(), n, EVENTFUL_OBJECT_PURELY_LOCAL)); } } } private void computeEventful() { /* * Topological order of Eventize DAG */ String[] order = new String[eventizes.size()]; /* * Perform topological sort */ int white = 0, gray = 1, black = 2; int last = eventizes.size() - 1; Map<String, Integer> color = new HashMap<String, Integer>(); Stack<String> dfsStack = new Stack<String>(); /* * Initialize color. * Some types are only on one or the other side of the * inference. */ for (String r : eventizes.keySet()) { color.put(r, white); for (String s : eventizes.get(r)) { color.put(s, white); } } int indx = 0; for (String s : eventizes.keySet()) { dfsStack.push(s); while (dfsStack.size() > 0) { String top = dfsStack.pop(); if (!color.containsKey(top)) { continue; } if (color.get(top) == white) { color.put(top, gray); dfsStack.push(top); // for v in Adj[s] if (eventizes.containsKey(top)) { for (String v : eventizes.get(top)) { if (color.get(v) == white) { dfsStack.push(v); } } } } else if (color.get(top) == gray && eventizes.containsKey(top)) { order[last - indx] = top; ++indx; color.put(top, black); } } } /* * Propagate eventfulness by iterating in topological order */ for (String s : order) { if (eventfulTypes.contains(typeRegistry.getType(s))) { for (String v : eventizes.get(s)) { eventfulTypes.add(typeRegistry.getType(v)); } } } } private JS

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> (JSType type : ut.getAlternates()) { if (type.isObject()) { addEventizeClass(className, type); } } } else { addEventizeClass(className, thatType); } } private void addEventizeClass(String className, JSType thatType) { String propertyJsTypeName = thatType.getDisplayName(); Set<String> eventize = eventizes.get(propertyJsTypeName); if (eventize == null) { eventize = new HashSet<String>(); eventizes.put(propertyJsTypeName, eventize); } eventize.add(className); } @Override public void enterScope(NodeTraversal t) { Node n = t.getScopeRoot(); boolean isConstructor = false; boolean isInDisposal = false; String functionName = null; /* * Scope entered is a function definition */ if (n.isFunction()) { functionName = NodeUtil.getFunctionName(n); /* * Skip anonymous functions */ if (functionName != null) { JSDocInfo jsDocInfo = NodeUtil.getBestJSDocInfo(n); if (jsDocInfo != null) { /* * Record constructor of a type */ if (jsDocInfo.isConstructor()) { isConstructor = true; /* * Initialize eventizes relationship */ if (t.getScope() != null && t.getScope().getTypeOfThis() != null) { ObjectType objectType = ObjectType.cast(t.getScope() .getTypeOfThis().dereference()); /* * Eventize due to inheritance */ while (objectType != null) { objectType = objectType.getImplicitPrototype(); if (objectType == null) { break; } if (objectType.getDisplayName().endsWith("prototype")) { continue; } addEventize(compiler.getTypeRegistry().getType(functionName), objectType); /* * Don't add transitive eventize edges here, it will be * taken care of in computeEventful */ break; } } } } /* * Indicate within a disposeInternal member */ if (functionName.endsWith(".disposeInternal")) { isInDisposal = true; } } isConstructorStack.push(isConstructor); isDisposalStack.push(isInDisposal); } else { isConstructorStack.push(inConstructorScope()); isDisposalStack.push(inDisposalScope()); } } @Override public void exitScope(NodeTraversal t) { isConstructorStack.pop(); isDisposalStack.pop(); } /* * Is the current node a call to goog.events.unlisten */ private void isGoogEventsUnlisten(Node n) { Preconditions.checkArgument(n.getChildCount() > 3); Node listener = n.getChildAtIndex(3); Node objectWithListener = n.getChildAtIndex(1); if (!objectWithListener.isQualifiedName()) { return; } if (listener.isFunction()) { /* * Anonymous function */ compiler.report(JSError.make(n.getSourceFileName(), n, UNLISTEN_

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>WITH_ANONBOUND)); } else if (listener.isCall()) { if (!listener.getFirstChild().isQualifiedName()) { /* * Anonymous function */ compiler.report(JSError.make(n.getSourceFileName(), n, UNLISTEN_WITH_ANONBOUND)); } else if (listener.getFirstChild().getQualifiedName() .equals("goog.bind")) { /* * Using goog.bind to unlisten */ compiler.report(JSError.make(n.getSourceFileName(), n, UNLISTEN_WITH_ANONBOUND)); } } } private void visitCall(NodeTraversal t, Node n) { Node functionCalled = n.getFirstChild(); if (functionCalled == null || !functionCalled.isQualifiedName()) { return; } String functionCalledName = functionCalled.getQualifiedName(); JSType typeOfThis = getTypeOfThisForScope(t); if (typeOfThis == null) { return; } /* * Class considered eventful if there is an unlisten call in the * disposal. */ if (functionCalledName.equals("goog.events.unlisten")) { if (inDisposalScope()) { eventfulTypes.add(typeOfThis); } isGoogEventsUnlisten(n); } if (inDisposalScope() && functionCalledName.equals("goog.events.removeAll")) { eventfulTypes.add(typeOfThis); } /* * If member with qualified name gets disposed of when this class * gets disposed, consider the member type as an eventizer of this * class. */ JSType disposedType = maybeReturnDisposedType(n, inDisposalScope()); if (!collectorFilterType(disposedType)) { addEventize(getTypeOfThisForScope(t), disposedType); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: visitCall(t, n); break; default: break; } } } private class Traversal extends AbstractPostOrderCallback implements ScopedCallback { /* * Checks if the input node correspond to the creation of an eventful object */ private boolean createsEventfulObject(Node n) { Node first = n.getFirstChild(); JSType type = n.getJSType(); if (first == null || !first.isQualifiedName() || type.isEmptyType() || type.isUnknownType()) { return false; } boolean isOfTypeNeedingDisposal = false; for (JSType disposableType : eventfulTypes) { if (type.isSubtype(disposableType)) { isOfTypeNeedingDisposal = true; break; } } return isOfTypeNeedingDisposal; } /* * This function traverses the current scope to see if a locally * defined eventful object is assigned to a live-out variable. * * Note: This function could be called multiple times to traverse * the same scope if multiple local eventful objects are created in the * scope. */ private Node localEventfulObjectAssign( NodeTraversal t, Node property

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Node) { Node parent; if (!t.getScope().isGlobal()) { /* * In function */ parent = NodeUtil.getFunctionBody(t.getScopeRoot()); } else { /* * In global scope */ parent = t.getScopeRoot().getFirstChild(); } /* * Check to see if locally created EventHandler is assigned to field */ for (Node sibling : parent.children()) { if (sibling.isExprResult()) { Node assign = sibling.getFirstChild(); if (assign.isAssign()) { // assign.getLastChild().isEquivalentTo(propertyNode) did not work if (propertyNode.getQualifiedName().equals(assign.getLastChild() .getQualifiedName())) { if (!assign.getFirstChild().isName()) { return assign.getFirstChild(); } } } } } /* * Eventful object created and assigned to a local variable which is not * assigned to another variable in a way to allow disposal. */ String key = generateKey(t, propertyNode, false); if (key == null) { return null; } EventfulObjectState e; if (eventfulObjectMap.containsKey(key)) { e = eventfulObjectMap.get(key); if (e.seen == SeenType.ALLOCATED) { e.seen = SeenType.ALLOCATED_LOCALLY; } } else { e = new EventfulObjectState(); e.seen = SeenType.ALLOCATED_LOCALLY; eventfulObjectMap.put(key, e); } e.allocationSite = propertyNode; return null; } /* * Record the creation of a new eventful object. */ private void visitNew(NodeTraversal t, Node n, Node parent) { if (!createsEventfulObject(n)) { return; } /* * Insert allocation site and construct into eventfulObjectMap */ String key; Node propertyNode; /* * Handles (E is an eventful class): * - object.something = new E(); * - local = new E(); * - var local = new E(); */ if (parent.isAssign()) { propertyNode = parent.getFirstChild(); } else { propertyNode = parent; } key = generateKey(t, propertyNode, false); if (key == null) { return; } EventfulObjectState e; if (eventfulObjectMap.containsKey(key)) { e = eventfulObjectMap.get(key); } else { e = new EventfulObjectState(); e.seen = SeenType.ALLOCATED; eventfulObjectMap.put(key, e); } e.allocationSite = propertyNode; /* * Check if locally defined eventful object is assigned to global variable * and create an entry mapping to the previous site. */ if (propertyNode.isName()) { Node globalVarNode = localEventfulObjectAssign(t, propertyNode); if (globalVarNode != null) { key = generateKey(t, globalVarNode, false); if (key == null) { /* * Local variable

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> is assigned to an array or in a manner requiring * a function call. */ e.seen = SeenType.POSSIBLY_DISPOSED; return; } eventfulObjectMap.put(key, e); } } } private void addDisposeArgumentsMatched(Map<String, List<Integer>> map, Node n, String property, List<Node> foundDisposeCalls) { for (String disposeMethod : map.keySet()) { if (property.endsWith(disposeMethod)) { List<Integer> disposeArguments = map.get(disposeMethod); // Dispose specific arguments only Node t = n.getNext(); int tsArgument = 0; for (Integer disposeArgument : disposeArguments) { switch (disposeArgument) { // Dispose all arguments case DISPOSE_ALL: for (Node tt = n.getNext(); tt != null; tt = tt.getNext()) { foundDisposeCalls.add(tt); } break; // Dispose objects called on case DISPOSE_SELF: Node calledOn = n.getFirstChild(); foundDisposeCalls.add(calledOn); break; default: // The current item pointed to by t is beyond that requested in // current array element. if (tsArgument > disposeArgument) { t = n.getNext(); tsArgument = 0; } for (; tsArgument < disposeArgument && t != null; ++tsArgument) { t = t.getNext(); } if (tsArgument == disposeArgument && t != null) { foundDisposeCalls.add(t); } break; } } } } } private List<Node> maybeGetValueNodesFromCall(Node n) { List<Node> ret = Lists.newArrayList(); Node first = n.getFirstChild(); if (first == null || !first.isQualifiedName()) { return ret; } String property = first.getQualifiedName(); Node base = first.getFirstChild(); JSType baseType = null; if (base != null) { baseType = base.getJSType(); } for (JSType key : disposeCalls.keySet()) { if (key == null || (baseType != null && isPossiblySubtype(baseType, key))) { addDisposeArgumentsMatched(disposeCalls.get(key), first, property, ret); } } return ret; } /* * Look for calls to an eventful object's disposal functions. * (dispose or removeAll will remove all event listeners from * an EventHandler). */ private void visitCall(NodeTraversal t, Node n) { // Filter the calls to find a "dispose" call List<Node> variableNodes = maybeGetValueNodesFromCall(n); for (Node variableNode : variableNodes) { Preconditions.checkState(variableNode != null); // Only consider removals on eventful object boolean isTrackedRemoval = false; JSType vnType = variableNode.getJSType(); for (JSType type : eventfulTypes) { if (isPossiblySubtype(vnType, type)) { isTrackedRemoval = true; } } if (!isTrackedRemoval) { continue; } String key = generate

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Key(t, variableNode, false); if (key == null) { continue; } eventfulObjectDisposed(t, variableNode); } } /** * Dereference a type, autoboxing it and filtering out null. * From {@link CheckAccessControls} */ private JSType dereference(JSType type) { return type == null ? null : type.dereference(); } /* * Check function definitions to add custom dispose methods. */ public void visitFunction(NodeTraversal t, Node n) { Preconditions.checkArgument(n.isFunction()); JSDocInfo jsDocInfo = NodeUtil.getFunctionJSDocInfo(n); // Function annotated to dispose of if (jsDocInfo != null && jsDocInfo.isDisposes()) { JSType type = n.getJSType(); if (type == null || type.isUnknownType()) { return; } FunctionType funType = type.toMaybeFunctionType(); Node paramNode = NodeUtil.getFunctionParameters(n).getFirstChild(); List<Integer> positionalDisposedParameters = Lists.newArrayList(); if (jsDocInfo.disposesOf("*")) { positionalDisposedParameters.add(DISPOSE_ALL); } else { // Parameter types int index = 0; for (Node p : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } if (jsDocInfo.disposesOf(paramNode.getString())) { positionalDisposedParameters.add(index); } paramNode = paramNode.getNext(); index++; } } addDisposeCall(NodeUtil.getFunctionName(n), positionalDisposedParameters); } } /* * Track assignments to see if a private field is being * overwritten. * * Assigning to an array element is taken care of by the generateKey * returning null on array ("complex") variable names. */ public void visitAssign(NodeTraversal t, Node n) { Node assignedTo = n.getFirstChild(); JSType assignedToType = assignedTo.getJSType(); if (assignedToType == null || assignedToType.isEmptyType()) { return; } if (n.getFirstChild().isGetProp()) { boolean isTrackedAssign = false; for (JSType disposalType : eventfulTypes) { if (assignedToType.isSubtype(disposalType)) { isTrackedAssign = true; break; } } if (!isTrackedAssign) { return; } JSDocInfo di = n.getJSDocInfo(); ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getFirstChild() .getJSType())); String propertyName = n.getFirstChild().getLastChild().getString(); boolean fieldIsPrivate = ( (di != null) && (di.getVisibility() == Visibility.PRIVATE)); /* * See if field is defined as private in superclass */ while (objectType != null) { di = null; objectType = objectType.getImplicitPrototype(); if (objectType == null) { break; } /* * Skip prototype definitions:

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * Don't flag a field declared private in assignment as well * as in prototype declaration * Assumption: The inheritance hierarchy is similar to * class * class.prototype * superclass * superclass.prototype */ if (objectType.getDisplayName().endsWith("prototype")) { continue; } di = objectType.getOwnPropertyJSDocInfo(propertyName); if (di != null) { if (fieldIsPrivate || di.getVisibility() == Visibility.PRIVATE) { compiler.report( t.makeError(n, OVERWRITE_PRIVATE_EVENTFUL_OBJECT)); break; } } } } } /* * Filter out any eventful objects returned. */ private void visitReturn(NodeTraversal t, Node n) { Node variableNode = n.getFirstChild(); if (variableNode == null) { return; } if (!variableNode.isArrayLit()) { eventfulObjectDisposed(t, variableNode); } else { for (Node child : variableNode.children()) { eventfulObjectDisposed(t, child); } } } /* * Mark an eventful object as being disposed. */ private void eventfulObjectDisposed(NodeTraversal t, Node variableNode) { String key = generateKey(t, variableNode, false); if (key == null) { return; } EventfulObjectState e = eventfulObjectMap.get(key); if (e == null) { e = new EventfulObjectState(); eventfulObjectMap.put(key, e); } e.seen = SeenType.POSSIBLY_DISPOSED; } @Override public void enterScope(NodeTraversal t) { /* * Local variables captured in scope are filtered at present. * LiveVariableAnalysis used to filter such variables. */ ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, t.getScope(), compiler); liveness.analyze(); for (Var v : liveness.getEscapedLocals()) { eventfulObjectDisposed(t, v.getNode()); } } @Override public void exitScope(NodeTraversal t) { } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.ASSIGN: visitAssign(t, n); break; case Token.CALL: visitCall(t, n); break; case Token.FUNCTION: visitFunction(t, n); break; case Token.NEW: visitNew(t, n, parent); break; case Token.RETURN: visitReturn(t, n); break; default: break; } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Void type whose only element is the {@code undefined} value. */ public class VoidType extends ValueType { private static final long serialVersionUID = 1L; VoidType(JSTypeRegistry registry) { super(registry); } @Override public JSType restrictByNotNullOrUndefined() { return registry.getNativeType(JSTypeNative.NO_TYPE); } @Override public TernaryValue testForEquality(JSType that) { if (UNKNOWN.equals(super.testForEquality(that))) { return UNKNOWN; } if (that.isSubtype(this) || that.isSubtype(getNativeType(JSTypeNative.NULL_TYPE))) { return TRUE; } return FALSE; } @Override public boolean matchesNumberContext() { return false; } @Override public boolean matchesObjectContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public boolean isVoidType() { return true; } @Override String toStringHelper(boolean forAnnotations

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>) { return getDisplayName(); } @Override public String getDisplayName() { return "undefined"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.FALSE; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseVoidType(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2005 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.deps.DependencyInfo; import com.google.javascript.jscomp.deps.SortedDependencies; import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; /** * A JavaScript module has a unique name, consists of a list of compiler inputs, * and can depend on other modules. * */ public class JSModule implements DependencyInfo, Serializable { private static final long serialVersionUID = 1; static final DiagnosticType CIRCULAR_DEPENDENCY_ERROR = DiagnosticType.error("JSC_CIRCULAR_DEP", "Circular dependency detected: {0}"); /** Module name */ private final String name; /** Source code inputs */ private final List<CompilerInput> inputs = new ArrayList<CompilerInput>(); /** Modules that this module depends on */ private final List<JSModule> deps = new ArrayList<JSModule>(); private int depth; /** * Creates an instance. * * @param name A unique name for the module */ public JSModule(String name) { this.name = name; this.depth = -1; } /** Gets the module name. */ @Override public String getName() { return name; } @Override public List<String> getProvides() { return ImmutableList.<String>of(name); } @Override public List<String> getRequires() { ImmutableList.Builder<String> builder = ImmutableList.builder(); for (JSModule m : deps) { builder.add(m.getName()); } return builder.build(); } @Override public String getPathRelativeToClosureBase() { throw new UnsupportedOperationException(); } /** Adds a source file input to this module. */ public void add(SourceFile file) { add(new CompilerInput(file)); } /** Adds a source file input to this module. */ public void addFirst(SourceFile file) { addFirst(new CompilerInput(file)); } /** Adds a source code input to this module. */ public void add(CompilerInput input

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>) { inputs.add(input); input.setModule(this); } /** * Adds a source code input to this module. Call only if the input might * already be associated with a module. Otherwise, use * add(CompilerInput input). */ void addAndOverrideModule(CompilerInput input) { inputs.add(input); input.overrideModule(this); } /** Adds a source code input to this module. */ public void addFirst(CompilerInput input) { inputs.add(0, input); input.setModule(this); } /** Adds a source code input to this module directly after other. */ public void addAfter(CompilerInput input, CompilerInput other) { Preconditions.checkState(inputs.contains(other)); inputs.add(inputs.indexOf(other), input); input.setModule(this); } /** Adds a dependency on another module. */ public void addDependency(JSModule dep) { Preconditions.checkNotNull(dep); Preconditions.checkState(dep != this); deps.add(dep); } /** Removes an input from this module. */ public void remove(CompilerInput input) { input.setModule(null); inputs.remove(input); } /** Removes all of the inputs from this module. */ public void removeAll() { for (CompilerInput input : inputs) { input.setModule(null); } inputs.clear(); } /** * Gets the list of modules that this module depends on. * * @return A list that may be empty but not null */ public List<JSModule> getDependencies() { return deps; } /** * Gets the names of the modules that this module depends on, * sorted alphabetically. */ List<String> getSortedDependencyNames() { List<String> names = Lists.newArrayList(); for (JSModule module : getDependencies()) { names.add(module.getName()); } Collections.sort(names); return names; } /** * Returns the transitive closure of dependencies starting from the * dependencies of this module. */ public Set<JSModule> getAllDependencies() { Set<JSModule> allDeps = Sets.newHashSet(deps); List<JSModule> workList = Lists.newArrayList(deps); while (workList.size() > 0) { JSModule module = workList.remove(workList.size() - 1); for (JSModule dep : module.getDependencies()) { if (allDeps.add(dep)) { workList.add(dep); } } } return allDeps; } /** Returns this module and all of its dependencies in one list. */ public Set<JSModule> getThisAndAllDependencies() { Set<JSModule> deps = getAllDependencies(); deps.add(this); return deps; } /** * Gets this module's list of source code inputs. * * @return A list that may be empty but not null */ public List<CompilerInput> getInputs() { return inputs; } /** Returns the input with the given name or null if none. */ public CompilerInput getByName(String name) { for (

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>CompilerInput input : inputs) { if (name.equals(input.getName())) { return input; } } return null; } /** * Removes any input with the given name. Returns whether any were removed. */ public boolean removeByName(String name) { boolean found = false; Iterator<CompilerInput> iter = inputs.iterator(); while (iter.hasNext()) { CompilerInput file = iter.next(); if (name.equals(file.getName())) { iter.remove(); file.setModule(null); found = true; } } return found; } /** Returns the module name (primarily for debugging). */ @Override public String toString() { return name; } /** * Removes any references to nodes of the AST. This method is needed to * allow the ASTs to be garbage collected if the modules are kept around. */ public void clearAsts() { for (CompilerInput input : inputs) { input.clearAst(); } } /** * Puts the JS files into a topologically sorted order by their dependencies. */ public void sortInputsByDeps(Compiler compiler) { // Set the compiler, so that we can parse requires/provides and report // errors properly. for (CompilerInput input : inputs) { input.setCompiler(compiler); } // Sort the JSModule in this order. try { List<CompilerInput> sortedList = (new SortedDependencies<CompilerInput>( Collections.<CompilerInput>unmodifiableList(inputs))) .getSortedList(); inputs.clear(); inputs.addAll(sortedList); } catch (CircularDependencyException e) { compiler.report( JSError.make(CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); } } /** * Returns the given collection of modules in topological order. * * Note that this will return the modules in the same order if they are * already sorted, and in general, will only change the order as necessary to * satisfy the ordering dependencies. This can be important for cases where * the modules do not properly specify all dependencies. */ public static JSModule[] sortJsModules(Collection<JSModule> modules) throws CircularDependencyException { // Sort the JSModule in this order. List<JSModule> sortedList = (new SortedDependencies<JSModule>( Lists.newArrayList(modules))).getSortedList(); return sortedList.toArray(new JSModule[sortedList.size()]); } /** * @param dep the depth to set */ public void setDepth(int dep) { this.depth = dep; } /** * @return the depth */ public int getDepth() { return depth; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A helper class for passes that want to access all information about where a * variable is referenced and declared at once and then make a decision as to * how it should be handled, possibly inlining, reordering, or generating * warnings. Callers do this by providing {@link Behavior} and then * calling {@link #process(Node, Node)}. * * @author kushal@google.com (Kushal Dave) */ class ReferenceCollectingCallback implements ScopedCallback, HotSwapCompilerPass, StaticSymbolTable<Var, ReferenceCollectingCallback.Reference> { /** * Maps a given variable to a collection of references to that name. Note that * Var objects are not stable across multiple traversals (unlike scope root or * name). */ private final Map<Var, ReferenceCollection> referenceMap = Maps.newHashMap(); /** * The stack of basic blocks and scopes the current traversal is in. */ private final Deque<BasicBlock> blockStack = new ArrayDeque<BasicBlock>(); /** * Source of behavior at various points in the traversal. */ private final Behavior behavior; /** * JavaScript compiler to use in traversing. */ private final AbstractCompiler compiler; /** * Only collect references for filtered variables. */ private final Predicate<

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Var> varFilter; /** * Constructor initializes block stack. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) { this(compiler, behavior, Predicates.<Var>alwaysTrue()); } /** * Constructor only collects references that match the given variable. * * The test for Var equality uses reference equality, so it's necessary to * inject a scope when you traverse. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior, Predicate<Var> varFilter) { this.compiler = compiler; this.behavior = behavior; this.varFilter = varFilter; } /** * Convenience method for running this pass over a tree with this * class as a callback. */ @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } /** * Same as process but only runs on a part of AST associated to one script. */ @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } /** * Gets the variables that were referenced in this callback. */ @Override public Iterable<Var> getAllSymbols() { return referenceMap.keySet(); } @Override public Scope getScope(Var var) { return var.scope; } /** * Gets the reference collection for the given variable. */ @Override public ReferenceCollection getReferences(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null && varFilter.apply(v)) { addReference(v, new Reference(n, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ @Override public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode(); BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); blockStack.push(new BasicBlock(parent, n)); } /** * Updates block stack and invokes any additional behavior. */ @Override public void exitScope(NodeTraversal t) { blockStack.pop(); if (t.getScope().isGlobal()) { // Update global scope reference lists when we are done with it. compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); behavior.afterExitScope(t, compiler.getGlobalVarReferences()); } else { behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); } } /** * Updates block stack. */ @Override public boolean shouldTraverse(NodeTraversal

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @return true if this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explicit CATCH token but no explicit // FINALLY token. For simplicity, we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.isCase(); } private void addReference(Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference); } interface ReferenceMap { ReferenceCollection getReferences(Var var); } private static class ReferenceMapWrapper implements ReferenceMap { private final Map<Var, ReferenceCollection> referenceMap; public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) { this.referenceMap = referenceMap; } @Override public ReferenceCollection getReferences(Var var) { return referenceMap.get(var); } } /** * Way for callers to add specific behavior during traversal that * utilizes the built-up reference information. */ interface Behavior { /** * Called after we finish with a scope. */ void afterExitScope(NodeTraversal t, ReferenceMap referenceMap); } static final Behavior DO_NOTHING_BEHAVIOR = new Behavior() { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {} }; /** * A collection of references. Can be subclassed to apply checks or * store additional state when adding. */ static class ReferenceCollection implements Iterable<Reference> { List<Reference> references = Lists.newArrayList(); @Override public Iterator<Reference> iterator() { return references.iterator(); } void add(Reference reference) { references.add(reference); } /** * Determines if the variable for this reference collection is * "well-defined." A variable is well-defined if we can prove at * compile-time that it's assigned a value before it's used. * * Notice that if this function returns false, this doesn't imply that the * variable is used before it's assigned. It just means

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> input.getInputId()); } private Reference(Node nameNode, BasicBlock basicBlock, Scope scope, InputId inputId) { this.nameNode = nameNode; this.basicBlock = basicBlock; this.scope = scope; this.inputId = inputId; this.sourceFile = nameNode.getStaticSourceFile(); } /** * Makes a copy of the current reference using a new Scope instance. */ Reference cloneWithNewScope(Scope newScope) { return new Reference(nameNode, basicBlock, newScope, inputId); } @Override public Var getSymbol() { return scope.getVar(nameNode.getString()); } @Override public Node getNode() { return nameNode; } public InputId getInputId() { return inputId; } @Override public StaticSourceFile getSourceFile() { return sourceFile; } boolean isDeclaration() { Node parent = getParent(); Node grandparent = parent.getParent(); return DECLARATION_PARENTS.contains(parent.getType()) || parent.isParamList() && grandparent.isFunction(); } boolean isVarDeclaration() { return getParent().isVar(); } boolean isHoistedFunction() { return NodeUtil.isHoistedFunctionDeclaration(getParent()); } /** * Determines whether the variable is initialized at the declaration. */ boolean isInitializingDeclaration() { // VAR is the only type of variable declaration that may not initialize // its variable. Catch blocks, named functions, and parameters all do. return isDeclaration() && !getParent().isVar() || nameNode.getFirstChild() != null; } /** * @return For an assignment, variable declaration, or function declaration * return the assigned value, otherwise null. */ Node getAssignedValue() { Node parent = getParent(); return (parent.isFunction()) ? parent : NodeUtil.getAssignedValue(nameNode); } BasicBlock getBasicBlock() { return basicBlock; } Node getParent() { return getNode().getParent(); } Node getGrandparent() { Node parent = getParent(); return parent == null ? null : parent.getParent(); } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.isVar()) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { Node parent = getParent(); return parent.isAssign() && parent.getFirstChild() == nameNode; } boolean isLvalue() { Node parent = getParent(); int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iter

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> private InferJSDocInfo inferJSDocInfo = null; // These fields are used to calculate the percentage of expressions typed. private int typedCount = 0; private int nullCount = 0; private int unknownCount = 0; private boolean inExterns; // A state boolean to see we are currently in @notypecheck section of the // code. private int noTypeCheckSection = 0; private Method editDistance; private static final class SuggestionPair { private final String suggestion; final int distance; private SuggestionPair(String suggestion, int distance) { this.suggestion = suggestion; this.distance = distance; } } public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, Scope topScope, MemoizedScopeCreator scopeCreator, CheckLevel reportMissingOverride) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.reverseInterpreter = reverseInterpreter; this.typeRegistry = typeRegistry; this.topScope = topScope; this.scopeCreator = scopeCreator; this.reportMissingOverride = reportMissingOverride; this.reportUnknownTypes = ((Compiler) compiler).getOptions().enables( DiagnosticGroups.REPORT_UNKNOWN_TYPES); this.inferJSDocInfo = new InferJSDocInfo(compiler); ClassLoader classLoader = TypeCheck.class.getClassLoader(); try { Class<?> c = classLoader.loadClass( "com.google.common.string.EditDistance"); editDistance = c.getDeclaredMethod( "getEditDistance", String.class, String.class, boolean.class); } catch (Exception ignored) { editDistance = null; } } public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, CheckLevel reportMissingOverride) { this(compiler, reverseInterpreter, typeRegistry, null, null, reportMissingOverride); } TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry) { this(compiler, reverseInterpreter, typeRegistry, null, null, CheckLevel.WARNING); } /** Turn on the missing property check. Returns this for easy chaining. */ TypeCheck reportMissingProperties(boolean report) { reportMissingProperties = report; return this; } /** * Main entry point for this phase of processing. This follows the pattern for * JSCompiler phases. * * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ @Override public void process(Node externsRoot, Node jsRoot) { Preconditions.checkNotNull(scopeCreator); Preconditions.checkNotNull(topScope); Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); if (externsRoot != null) { check(externsRoot, true); } check(jsRoot, false); } /** Main entry point of this phase for testing code. */ public Scope processForTesting(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Node externsRoot, Node jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } private void checkNoTypeCheckSection(Node n, boolean enterSection) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.VAR: case Token.FUNCTION: case Token.ASSIGN: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { if (enterSection) { noTypeCheckSection++; } else { noTypeCheckSection--; } } validator.setShouldReport(noTypeCheckSection == 0); break; } } private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType, String... arguments) { if (noTypeCheckSection == 0) { t.report(n, diagnosticType, arguments); } } @Override public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { checkNoTypeCheckSection(n, true); switch (n.getType()) { case Token.FUNCTION: // normal type checking final Scope outerScope = t.getScope(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { report(t, n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * *

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.CAST: Node expr = n.getFirstChild(); JSType exprType = getJSType(expr); JSType castType = getJSType(n); // TODO(johnlenz): determine if we can limit object literals in some // way. if (!expr.isObjectLit()) { validator.expectCanCast(t, n, castType, exprType); } ensureTyped(t, n, castType); if (castType.isSubtype(exprType) || expr.isObjectLit()) { expr.setJSType(castType); } break; case Token.NAME: typeable = visitName(t, n, parent); break; case Token.PARAM_LIST: typeable = false; break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: ensureTyped(t, n, NUMBER_TYPE); break; case Token.STRING: ensureTyped(t, n, STRING_TYPE); break; case Token.STRING_KEY: typeable = false; break; case Token.GETTER_DEF: case Token.SETTER_DEF: // Object literal keys are handled with OBJECTLIT break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.isAssign() && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); break; case Token.CALL: visitCall(t, n); typeable = !parent.isExprResult(); break; case Token.RETURN: visitReturn(t, n); typeable = false

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject(t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior. case Token.LABEL: case Token.LABEL_NAME: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT_CASE: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.IF: case Token.WHILE: typeable = false; break; case Token.FOR: if (NodeUtil.isForIn(n)) { Node obj = n.getChildAtIndex(1); if (getJSType(obj).isStruct()) { report(t, obj, IN_USED_WITH_STRUCT); } } typeable = false; break; // These nodes are typed during the type inference. case Token.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t, n); } else { // If this is an enum, then give that type to the objectlit as well. if ((n.isObjectLit()) && (parent.getJSType() instanceof EnumType)) { ensureTyped(t, n, parent.getJSType()); } else { ensureTyped(t, n); } } if (n.isObjectLit()) { JSType typ = getJSType(n); for (Node key : n.children()) { visitObjLitKey(t, key, n, typ); } } break; default: report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType())); ensureTyped(t, n); break; } // Don't count externs since the user's code may not even use that part. typeable = typeable && !inExterns; if (typeable) { doPercentTypedAccounting(t, n); } checkNoTypeCheckSection(n, false); } private void checkTypeofString(NodeTraversal t, Node n, String s) { if (!(s.equals("number") || s.equals("string") || s.equals("boolean") || s.equals("undefined") || s.equals("function") || s.equals("object") || s

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> methods are implemented. // // As-is, this misses many other ways to override a property. // // object.prototype.property = ...; if (object.isGetProp()) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = getJSType(object2); if (jsType.isFunctionType()) { FunctionType functionType = jsType.toMaybeFunctionType(); if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, propertyType); } } } } } /** * Visits an object literal field definition <code>key : value</code>. * * If the <code>lvalue</code> is a prototype modification, we change the * schema of the object type it is referring to. * * @param t the traversal * @param key the assign node */ private void visitObjLitKey( NodeTraversal t, Node key, Node objlit, JSType litType) { // Do not validate object lit value types in externs. We don't really care, // and it makes it easier to generate externs. if (objlit.isFromExterns()) { ensureTyped(t, key); return; } // Structs must have unquoted keys and dicts must have quoted keys if (litType.isStruct() && key.isQuotedString()) { report(t, key, ILLEGAL_OBJLIT_KEY, "struct"); } else if (litType.isDict() && !key.isQuotedString()) { report(t, key, ILLEGAL_OBJLIT_KEY, "dict"); } // TODO(johnlenz): Validate get and set function declarations are valid // as is the functions can have "extraneous" bits. // For getter and setter property definitions the // r-value type != the property type. Node rvalue = key.getFirstChild(); JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType( key, getJSType(rvalue)); if (rightType == null) { rightType = getNativeType(UNKNOWN_TYPE); } Node owner = objlit; // Validate value is assignable to the key type. JSType keyType = getJSType(key); JSType allowedValueType = keyType; if (allowedValueType.isEnumElementType()) { allowedValueType = allowedValueType.toMaybeEnumElementType().getPrimitiveType(); } boolean valid = validator.expectCanAssignToPropertyOf(t, key, rightType, allowedValueType, owner, NodeUtil.getObjectLitKeyName(key)); if (valid) { ensureTyped(t, key, rightType); } else { ensureTyped(t, key); } // Validate that the key type is assignable to the object property type. // This is necessary as the objlit may have been cast to a non-literal // object type. // TODO(johnlenz): consider introducing a CAST node to the AST (or // perhaps a parentheses node). JSType objlit

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Type = getJSType(objlit); ObjectType type = ObjectType.cast( objlitType.restrictByNotNullOrUndefined()); if (type != null) { String property = NodeUtil.getObjectLitKeyName(key); if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, key, keyType, type.getPropertyType(property), owner, property); } return; } } /** * Returns true if any type in the chain has an implicitCast annotation for * the given property. */ private static boolean propertyIsImplicitCast(ObjectType type, String prop) { for (; type != null; type = type.getImplicitPrototype()) { JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } } return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override iff the property is declared on a * superclass. Several checks regarding inheritance correctness are also * performed. */ private void checkDeclaredPropertyInheritance( NodeTraversal t, Node n, FunctionType ctorType, String propertyName, JSDocInfo info, JSType propertyType) { // If the supertype doesn't resolve correctly, we've warned about this // already. if (hasUnknownOrEmptySupertype(ctorType)) { return; } FunctionType superClass = ctorType.getSuperClassConstructor(); boolean superClassHasProperty = superClass != null && superClass.getInstanceType().hasProperty(propertyName); boolean superClassHasDeclaredProperty = superClass != null && superClass.getInstanceType().isPropertyTypeDeclared(propertyName); // For interface boolean superInterfaceHasProperty = false; boolean superInterfaceHasDeclaredProperty = false; if (ctorType.isInterface()) { for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { superInterfaceHasProperty = superInterfaceHasProperty || interfaceType.hasProperty(propertyName); superInterfaceHasDeclaredProperty = superInterfaceHasDeclaredProperty || interfaceType.isPropertyTypeDeclared(propertyName); } } boolean declaredOverride = info != null && info.isOverride(); boolean foundInterfaceProperty = false; if (ctorType.isConstructor()) { for (JSType implementedInterface : ctorType.getAllImplementedInterfaces()) { if (implementedInterface.isUnknownType() || implementedInterface.isEmptyType()) { continue; } FunctionType interfaceType = implementedInterface.toObjectType().getConstructor(); Preconditions.checkNotNull(interfaceType); boolean interfaceHasProperty = interfaceType.getPrototype().hasProperty(propertyName); foundInterfaceProperty = foundInterfaceProperty || interfaceHasProperty; if (reportMissingOverride.isOn() && !declaredOverride && interfaceHasProperty) { // @override not present, but the property does override an interface // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_INTERFACE_PROPERTY, propertyName, interfaceType.getTopMostDefiningType(propertyName).toString())); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * An unresolved type that was forward declared. So we know it exists, * but that it wasn't pulled into this binary. * * In most cases, it behaves like a bottom type in the type lattice: * no real type should be assigned to a NoResolvedType, but the * NoResolvedType is a subtype of everything. In a few cases, it behaves * like the unknown type: properties of this type are also NoResolved types, * and comparisons to other types always have an unknown result. * * @author nicksantos@google.com (Nick Santos) */ class NoResolvedType extends NoType { private static final long serialVersionUID = 1L; NoResolvedType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNoResolvedType() { return true; } @Override public boolean isNoType() { return false; } @Override public boolean isConstructor() { return false; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return !that.isNoType(); } } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "NoResolvedType"; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.ImmutableSet; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.regex.RegExpTree; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable, and checks that regular expressions * are syntactically valid. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning( "JSC_MALFORMED_REGEXP", "Malformed Regular Expression: {0}"); private static final Set<String> REGEXP_PROPERTY_BLACKLIST = ImmutableSet.of( "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$_", "$input", // The following would also be blacklisted, but they aren't valid // identifiers, so can't be accessed with the '.' operator anyway. // "$*", "$&", "$+", "$`", "$'", "input", "lastMatch", "lastParen", "leftContext", "rightContext", "global", "ignoreCase", "lastIndex", "multiline", "source"); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>parentType == Token.INSTANCEOF && !first) || parentType == Token.EQ || parentType == Token.NE || parentType == Token.SHEQ || parentType == Token.SHNE || parentType == Token.CASE || (parentType == Token.GETPROP && first && !REGEXP_PROPERTY_BLACKLIST.contains( parent.getLastChild().getString())))) { t.report(n, REGEXP_REFERENCE); globalRegExpPropertiesUsed = true; } } // Check the syntax of regular expression patterns. } else if (n.isRegExp()) { String pattern = n.getFirstChild().getString(); String flags = n.getChildCount() == 2 ? n.getLastChild().getString() : ""; try { RegExpTree.parseRegExp(pattern, flags); } catch (IllegalArgumentException ex) { t.report(n, MALFORMED_REGEXP, ex.getMessage()); } catch (IndexOutOfBoundsException ex) { t.report(n, MALFORMED_REGEXP, ex.getMessage()); } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> if (comment.getCommentType() == CommentType.JSDOC && !irFactory.parsedComments.contains(comment)) { irFactory.handlePossibleFileOverviewJsDoc(comment, irNode); } else if (comment.getCommentType() == CommentType.BLOCK_COMMENT) { irFactory.handleBlockComment(comment); } } } irFactory.setFileOverviewJsDoc(irNode); return irNode; } private void setFileOverviewJsDoc(Node irNode) { // Only after we've seen all @fileoverview entries, attach the // last one to the root node, and copy the found license strings // to that node. JSDocInfo rootNodeJsDoc = rootNodeJsDocHolder.getJSDocInfo(); if (rootNodeJsDoc != null) { irNode.setJSDocInfo(rootNodeJsDoc); rootNodeJsDoc.setAssociatedNode(irNode); } if (fileOverviewInfo != null) { if ((irNode.getJSDocInfo() != null) && (irNode.getJSDocInfo().getLicense() != null)) { fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); } irNode.setJSDocInfo(fileOverviewInfo); fileOverviewInfo.setAssociatedNode(irNode); } } private Node transformBlock(AstNode node) { Node irNode = transform(node); if (!irNode.isBlock()) { if (irNode.isEmpty()) { irNode.setType(Token.BLOCK); irNode.setWasEmptyNode(true); } else { Node newBlock = newNode(Token.BLOCK, irNode); newBlock.setLineno(irNode.getLineno()); newBlock.setCharno(irNode.getCharno()); maybeSetLengthFrom(newBlock, node); irNode = newBlock; } } return irNode; } /** * Check to see if the given block comment looks like it should be JSDoc. */ private void handleBlockComment(Comment comment) { Pattern p = Pattern.compile("(/|(\n[ \t]*))\\*[ \t]*@[a-zA-Z]+[ \t\n{]"); if (p.matcher(comment.getValue()).find()) { errorReporter.warning( SUSPICIOUS_COMMENT_WARNING, sourceName, comment.getLineno(), "", 0); } } /** * @return true if the jsDocParser represents a fileoverview. */ private boolean handlePossibleFileOverviewJsDoc( JsDocInfoParser jsDocParser) { if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) { fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo(); return true; } return false; } private void handlePossibleFileOverviewJsDoc(Comment comment, Node irNode) { JsDocInfoParser jsDocParser = createJsDocInfoParser(comment, irNode); parsedComments.add(comment); handlePossibleFileOverviewJsDoc(jsDocParser); } private JSDocInfo handleJsDoc(AstNode node,

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> node = newNode(transformTokenType(n.getType())); for (com.google.javascript.rhino.head.Node child : n) { node.addChildToBack(transform((AstNode) child)); } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret; if (n instanceof Name) { ret = transformNameAsString((Name) n); } else if (n instanceof NumberLiteral) { ret = transformNumberAsString((NumberLiteral) n); ret.putBooleanProp(Node.QUOTED_PROP, true); } else { ret = transform(n); ret.putBooleanProp(Node.QUOTED_PROP, true); } Preconditions.checkState(ret.isString()); return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.ARRAYLIT); for (AstNode child : literalNode.getElements()) { Node c = transform(child); node.addChildToBack(c); } return node; } @Override Node processAssignment(Assignment assignmentNode) { Node assign = processInfixExpression(assignmentNode); Node target = assign.getFirstChild(); if (!validAssignmentTarget(target)) { errorReporter.error( "invalid assignment target", sourceName, target.getLineno(), "", 0); } return assign; } @Override Node processAstRoot(AstRoot rootNode) { Node node = newNode(Token.SCRIPT); for (com.google.javascript.rhino.head.Node child : rootNode) { node.addChildToBack(transform((AstNode) child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * ECMA-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } } private boolean isDirective(Node n) { if (n == null) { return false; } int nType = n.getType(); return nType == Token.EXPR_RESULT && n.getFirstChild().isString() && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = newNode(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = newNode(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null) { errorReporter.error( "Catch clauses are not supported", sourceName, clauseNode.getCatchCondition().getLineno(), "", 0); } node.addChildToBack(transformBlock(clauseNode.getBody())); return node; } @Override Node processConditionalExpression(ConditionalExpression exprNode) { return newNode( Token.HOOK, transform(exprNode.getTestExpression()), transform(exprNode.getTrueExpression()), transform(exprNode.getFalseExpression())); } @Override Node processContinueStatement(ContinueStatement statementNode) { Node node = newNode(Token.CONTINUE); if (statementNode.getLabel() != null) { Node labelName = transform(statementNode.getLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processDoLoop(DoLoop loopNode) { return newNode( Token.DO, transformBlock(loopNode.getBody()), transform(loopNode.getCondition())); } @Override Node processElementGet(ElementGet getNode) { return newNode( Token.GETELEM, transform(getNode.getTarget()), transform(getNode.getElement())); } @Override Node processEmptyExpression(EmptyExpression exprNode) { Node node = newNode(Token.EMPTY); return node; } @Override Node processEmptyStatement(EmptyStatement exprNode) { Node node = newNode(Token.EMPTY); return node; } @Override Node processExpressionStatement(ExpressionStatement statementNode) { Node node = newNode(transformTokenType(statementNode.getType())); node.addChildToBack(transform(statementNode.getExpression())); return node; } @Override Node processForInLoop(ForInLoop loopNode) { if (loopNode.isForEach()) { errorReporter.error( "unsupported language extension: for each", sourceName, loopNode.getLineno(), "", 0); // Return the bare minimum to put the AST in a valid state. return newNode(Token.EXPR_RESULT, Node.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>newNumber(0)); } return newNode( Token.FOR, transform(loopNode.getIterator()), transform(loopNode.getIteratedObject()), transformBlock(loopNode.getBody())); } @Override Node processForLoop(ForLoop loopNode) { Node node = newNode( Token.FOR, transform(loopNode.getInitializer()), transform(loopNode.getCondition()), transform(loopNode.getIncrement())); node.addChildToBack(transformBlock(loopNode.getBody())); return node; } @Override Node processFunctionCall(FunctionCall callNode) { Node node = newNode(transformTokenType(callNode.getType()), transform(callNode.getTarget())); for (AstNode child : callNode.getArguments()) { node.addChildToBack(transform(child)); } node.setLineno(node.getFirstChild().getLineno()); node.setCharno(node.getFirstChild().getCharno()); maybeSetLengthFrom(node, callNode); return node; } @Override Node processFunctionNode(FunctionNode functionNode) { Name name = functionNode.getFunctionName(); Boolean isUnnamedFunction = false; if (name == null) { int functionType = functionNode.getFunctionType(); if (functionType != FunctionNode.FUNCTION_EXPRESSION) { errorReporter.error( "unnamed function statement", sourceName, functionNode.getLineno(), "", 0); // Return the bare minimum to put the AST in a valid state. return newNode(Token.EXPR_RESULT, Node.newNumber(0)); } name = new Name(); name.setIdentifier(""); isUnnamedFunction = true; } Node node = newNode(Token.FUNCTION); // if the function has an inline return annotation, attach it Node newName = transformNodeWithInlineJsDoc(name); if (isUnnamedFunction) { // Old Rhino tagged the empty name node with the line number of the // declaration. newName.setLineno(functionNode.getLineno()); // TODO(bowdidge) Mark line number of paren correctly. // Same problem as below - the left paren might not be on the // same line as the function keyword. int lpColumn = functionNode.getAbsolutePosition() + functionNode.getLp(); newName.setCharno(position2charno(lpColumn)); maybeSetLengthFrom(newName, name); } node.addChildToBack(newName); Node lp = newNode(Token.PARAM_LIST); // The left paren's complicated because it's not represented by an // AstNode, so there's nothing that has the actual line number that it // appeared on. We know the paren has to appear on the same line as the // function name (or else a semicolon will be inserted.) If there's no // function name, assume the paren was on the same line as the function. // TODO(bowdidge): Mark line number of paren correctly. Name fnName = functionNode.getFunctionName(); if (fnName != null) { lp.setLineno(fnName.getLineno()); } else { lp.setLineno(functionNode.getLin

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>eno()); } int lparenCharno = functionNode.getLp() + functionNode.getAbsolutePosition(); lp.setCharno(position2charno(lparenCharno)); for (AstNode param : functionNode.getParams()) { Node paramNode = transformNodeWithInlineJsDoc(param); // When in ideMode Rhino can generate a param list with only a single // ErrorNode. This is transformed into an EMPTY node. Drop this node in // ideMode to keep the AST in a valid state. if (paramNode.isName()) { lp.addChildToBack(paramNode); } else { // We expect this in ideMode or when there is an error handling // destructuring parameter assignments which aren't supported // (an error has already been reported). Preconditions.checkState( config.isIdeMode || paramNode.isObjectLit() || paramNode.isArrayLit()); } } node.addChildToBack(lp); Node bodyNode = transform(functionNode.getBody()); if (!bodyNode.isBlock()) { // When in ideMode Rhino tries to parse some constructs the compiler // doesn't support, repair it here. see Rhino's // Parser#parseFunctionBodyExpr. Preconditions.checkState(config.isIdeMode); bodyNode = IR.block(); } parseDirectives(bodyNode); node.addChildToBack(bodyNode); return node; } @Override Node processIfStatement(IfStatement statementNode) { Node node = newNode(Token.IF); node.addChildToBack(transform(statementNode.getCondition())); node.addChildToBack(transformBlock(statementNode.getThenPart())); if (statementNode.getElsePart() != null) { node.addChildToBack(transformBlock(statementNode.getElsePart())); } return node; } @Override Node processInfixExpression(InfixExpression exprNode) { Node n = newNode( transformTokenType(exprNode.getType()), transform(exprNode.getLeft()), transform(exprNode.getRight())); n.setLineno(exprNode.getLineno()); n.setCharno(position2charno(exprNode.getAbsolutePosition())); maybeSetLengthFrom(n, exprNode); return n; } @Override Node processKeywordLiteral(KeywordLiteral literalNode) { return newNode(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return newStringNode(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = newNode(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) { if (prev != null) { prev.addChildToBack(cur); } cur.addChildToBack(transform(label)); cur.setLineno(label.getLineno()); maybeSetLengthFrom(cur, label); int clauseAbsolutePosition = position2charno(label.getAbsolutePosition()); cur.setCharno(clauseAbsolutePosition); prev = cur; cur = newNode(Token.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>LABEL); } prev.addChildToBack(transform(statementNode.getStatement())); return node; } @Override Node processName(Name nameNode) { return processName(nameNode, false); } Node processName(Name nameNode, boolean asString) { if (asString) { return newStringNode(Token.STRING, nameNode.getIdentifier()); } else { if (isReservedKeyword(nameNode.getIdentifier())) { errorReporter.error( "identifier is a reserved word", sourceName, nameNode.getLineno(), "", 0); } return newStringNode(Token.NAME, nameNode.getIdentifier()); } } private boolean isAllowedProp(String identifier) { if (config.languageMode == LanguageMode.ECMASCRIPT3) { return !TokenStream.isKeyword(identifier); } return true; } private boolean isReservedKeyword(String identifier) { if (config.languageMode == LanguageMode.ECMASCRIPT3) { return TokenStream.isKeyword(identifier); } return reservedKeywords != null && reservedKeywords.contains(identifier); } @Override Node processNewExpression(NewExpression exprNode) { Node node = newNode( transformTokenType(exprNode.getType()), transform(exprNode.getTarget())); for (AstNode child : exprNode.getArguments()) { node.addChildToBack(transform(child)); } node.setLineno(exprNode.getLineno()); node.setCharno(position2charno(exprNode.getAbsolutePosition())); maybeSetLengthFrom(node, exprNode); return node; } @Override Node processNumberLiteral(NumberLiteral literalNode) { return newNumberNode(literalNode.getNumber()); } @Override Node processObjectLiteral(ObjectLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.OBJECTLIT); for (ObjectProperty el : literalNode.getElements()) { if (config.languageMode == LanguageMode.ECMASCRIPT3) { if (el.isGetter()) { reportGetter(el); continue; } else if (el.isSetter()) { reportSetter(el); continue; } } AstNode rawKey = el.getLeft(); Node key = transformAsString(rawKey); key.setType(Token.STRING_KEY); if (rawKey instanceof Name && !isAllowedProp(key.getString())) { errorReporter.warning(INVALID_ES3_PROP_NAME, sourceName, key.getLineno(), "", key.getCharno()); } Node value = transform(el.getRight()); if (el.isGetter()) { key.setType(Token.GETTER_DEF); Preconditions.checkState(value.isFunction()); if (getFnParamNode(value).hasChildren()) { reportGetterParam(el.getLeft()); } } else if (el.isSetter()) { key.setType(Token.SETTER_DEF); Preconditions.checkState(value.isFunction()); if (!getFnParamNode(value).hasOneChild()) { reportSetterParam(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>el.getLeft()); } } key.addChildToFront(value); node.addChildToBack(key); } return node; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ Node getFnParamNode(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } @Override Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); return node; } @Override Node processPropertyGet(PropertyGet getNode) { Node leftChild = transform(getNode.getTarget()); AstNode nodeProp = getNode.getProperty(); Node rightChild = transformAsString(nodeProp); if (nodeProp instanceof Name && !isAllowedProp( ((Name) nodeProp).getIdentifier())) { errorReporter.warning(INVALID_ES3_PROP_NAME, sourceName, rightChild.getLineno(), "", rightChild.getCharno()); } Node newNode = newNode( Token.GETPROP, leftChild, rightChild); newNode.setLineno(leftChild.getLineno()); newNode.setCharno(leftChild.getCharno()); maybeSetLengthFrom(newNode, getNode); return newNode; } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = newStringNode(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(literalStringNode, literalNode); Node node = newNode(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = newStringNode(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(flagsNode, literalNode); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = newNode(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode) { String value = literalNode.getValue(); Node n = newStringNode(value); if (value.indexOf('\u000B') != -1) { // NOTE(nicksantos): In JavaScript, there are 3 ways to // represent a vertical tab: \v, \x0B, \u000B. // The \v notation was added later, and is not understood // on IE. So we need to preserve it as-is. This is really //

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> obnoxious, because we do not have a good way to represent // how the original string was encoded without making the // representation of strings much more complicated. // // To handle this, we look at the original source test, and // mark the string as \v-encoded or not. If a string is // \v encoded, then all the vertical tabs in that string // will be encoded with a \v. int start = literalNode.getAbsolutePosition(); int end = start + literalNode.getLength(); if (start < sourceString.length() && (sourceString.substring( start, Math.min(sourceString.length(), end)) .indexOf("\\v") != -1)) { n.putBooleanProp(Node.SLASH_V, true); } } return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = newNode(Token.DEFAULT_CASE); } else { AstNode expr = caseNode.getExpression(); node = newNode(Token.CASE, transform(expr)); } Node block = newNode(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.setCharno(position2charno(caseNode.getAbsolutePosition())); maybeSetLengthFrom(block, caseNode); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node = newNode(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return newNode(Token.THROW, transform(statementNode.getExpression())); } @Override Node processTryStatement(TryStatement statementNode) { Node node = newNode(Token.TRY, transformBlock(statementNode.getTryBlock())); Node block = newNode(Token.BLOCK); boolean lineSet = false; for (CatchClause cc : statementNode.getCatchClauses()) { // Mark the enclosing block at the same line as the first catch // clause. if (lineSet == false) { block.setLineno(cc.getLineno()); maybeSetLengthFrom(block, cc); lineSet = true; } block.addChildToBack(transform(cc)); } node.addChildToBack(block); AstNode finallyBlock = statementNode.getFinallyBlock(); if (finallyBlock != null) { node.addChildToBack(transformBlock(finallyBlock)); } // If we didn't set the line on the catch clause, then // we've got an empty catch clause. Set its line to be the same // as the finally block (to match Old Rhino's behavior.) if ((lineSet ==

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> false) && (finallyBlock != null)) { block.setLineno(finallyBlock.getLineno()); maybeSetLengthFrom(block, finallyBlock); } return node; } @Override Node processUnaryExpression(UnaryExpression exprNode) { int type = transformTokenType(exprNode.getType()); Node operand = transform(exprNode.getOperand()); if (type == Token.NEG && operand.isNumber()) { operand.setDouble(-operand.getDouble()); return operand; } else { if (type == Token.DELPROP && !(operand.isGetProp() || operand.isGetElem() || operand.isName())) { String msg = "Invalid delete operand. Only properties can be deleted."; errorReporter.error( msg, sourceName, operand.getLineno(), "", 0); } else if (type == Token.INC || type == Token.DEC) { if (!validAssignmentTarget(operand)) { String msg = (type == Token.INC) ? "invalid increment target" : "invalid decrement target"; errorReporter.error( msg, sourceName, operand.getLineno(), "", 0); } } Node node = newNode(type, operand); if (exprNode.isPostfix()) { node.putBooleanProp(Node.INCRDECR_PROP, true); } return node; } } private boolean validAssignmentTarget(Node target) { switch (target.getType()) { case Token.CAST: // CAST is a bit weird, but syntactically valid. case Token.NAME: case Token.GETPROP: case Token.GETELEM: return true; } return false; } @Override Node processVariableDeclaration(VariableDeclaration declarationNode) { if (!config.acceptConstKeyword && declarationNode.getType() == com.google.javascript.rhino.head.Token.CONST) { processIllegalToken(declarationNode); } Node node = newNode(Token.VAR); for (VariableInitializer child : declarationNode.getVariables()) { node.addChildToBack(transform(child)); } return node; } @Override Node processVariableInitializer(VariableInitializer initializerNode) { Node node; Comment comment = initializerNode.getTarget().getJsDocNode(); // TODO(user): At some point, consider allowing only inline jsdocs for // variable initializers if (comment != null && !comment.getValue().contains("@")) { node = transformNodeWithInlineJsDoc(initializerNode.getTarget()); } else { node = transform(initializerNode.getTarget()); } if (initializerNode.getInitializer() != null) { Node initalizer = transform(initializerNode.getInitializer()); node.addChildToBack(initalizer); } return node; } @Override Node processWhileLoop(WhileLoop loopNode) { return newNode( Token.WHILE, transform(loopNode.getCondition()), transformBlock(loopNode.getBody())); } @Override Node processWithStatement(WithStatement statementNode) { return newNode( Token.WITH, transform(statementNode.getExpression()), transformBlock(statementNode.getStatement())); } @

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Override Node processIllegalToken(AstNode node) { errorReporter.error( "Unsupported syntax: " + com.google.javascript.rhino.head.Token.typeToName( node.getType()), sourceName, node.getLineno(), "", 0); return newNode(Token.EMPTY); } void reportDestructuringAssign(AstNode node) { errorReporter.error( "destructuring assignment forbidden", sourceName, node.getLineno(), "", 0); } void reportGetter(AstNode node) { errorReporter.error( GETTER_ERROR_MESSAGE, sourceName, node.getLineno(), "", 0); } void reportSetter(AstNode node) { errorReporter.error( SETTER_ERROR_MESSAGE, sourceName, node.getLineno(), "", 0); } void reportGetterParam(AstNode node) { errorReporter.error( "getters may not have parameters", sourceName, node.getLineno(), "", 0); } void reportSetterParam(AstNode node) { errorReporter.error( "setters must have exactly one parameter", sourceName, node.getLineno(), "", 0); } } private static int transformTokenType(int token) { switch (token) { case com.google.javascript.rhino.head.Token.RETURN: return Token.RETURN; case com.google.javascript.rhino.head.Token.BITOR: return Token.BITOR; case com.google.javascript.rhino.head.Token.BITXOR: return Token.BITXOR; case com.google.javascript.rhino.head.Token.BITAND: return Token.BITAND; case com.google.javascript.rhino.head.Token.EQ: return Token.EQ; case com.google.javascript.rhino.head.Token.NE: return Token.NE; case com.google.javascript.rhino.head.Token.LT: return Token.LT; case com.google.javascript.rhino.head.Token.LE: return Token.LE; case com.google.javascript.rhino.head.Token.GT: return Token.GT; case com.google.javascript.rhino.head.Token.GE: return Token.GE; case com.google.javascript.rhino.head.Token.LSH: return Token.LSH; case com.google.javascript.rhino.head.Token.RSH: return Token.RSH; case com.google.javascript.rhino.head.Token.URSH: return Token.URSH; case com.google.javascript.rhino.head.Token.ADD: return Token.ADD; case com.google.javascript.rhino.head.Token.SUB: return Token.SUB; case com.google.javascript.rhino.head.Token.MUL: return Token.MUL; case com.google.javascript.rhino.head.Token.DIV: return Token.DIV; case com.google.javascript.rhino.head.Token.MOD: return Token.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; /** * Value types (null, void, number, boolean, string). */ abstract class ValueType extends JSType { ValueType(JSTypeRegistry registry) { super(registry); } @Override final JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } @Override public boolean hasDisplayName() { return true; } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseValueType(this, that); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; /** * Sets the level for a particular DiagnosticGroup. * @author nicksantos@google.com (Nick Santos) */ public class DiagnosticGroupWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; private final DiagnosticGroup group; private final CheckLevel level; public DiagnosticGroupWarningsGuard( DiagnosticGroup group, CheckLevel level) { this.group = group; this.level = level; } @Override public CheckLevel level(JSError error) { return group.matches(error) ? level : null; } @Override public boolean disables(DiagnosticGroup otherGroup) { return !level.isOn() && group.isSubGroup(otherGroup); } @Override public boolean enables(DiagnosticGroup otherGroup) { if (level.isOn()) { for (DiagnosticType type : otherGroup.getTypes()) { if (group.matches(type)) { return true; } } } return false; } @Override public String toString() { return group + "(" + level + ")"; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import static com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt.LINE; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter; import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt; /** * Lightweight message formatter. The format of messages this formatter * produces is very compact and to the point. * */ public class LightweightMessageFormatter extends AbstractMessageFormatter { private SourceExcerpt excerpt; private static final ExcerptFormatter excerptFormatter = new LineNumberingFormatter(); /** * A constructor for when the client doesn't care about source information. */ private LightweightMessageFormatter() { super(null); this.excerpt = LINE; } public LightweightMessageFormatter(SourceExcerptProvider source) { this(source, LINE); } public LightweightMessageFormatter(SourceExcerptProvider source, SourceExcerpt excerpt) { super(source); Preconditions.checkNotNull(source); this.excerpt = excerpt; } static LightweightMessageFormatter withoutSource() { return new LightweightMessageFormatter(); } @Override public String formatError(JSError error) { return format(error, false); } @Override public String formatWarning(JSError warning) { return format(warning, true); } private String format(JSError error, boolean warning) { // extract source excerpt SourceExcerptProvider source = getSource(); String sourceExcerpt = source == null ? null : excerpt.get( source, error.sourceName, error.lineNumber, excerptFormatter); // formatting the message StringBuilder b = new StringBuilder(); if (error.sourceName != null) { b.append(error.sourceName); if (error.lineNumber > 0) { b.append(':'); b.append(error.lineNumber); } b.append(": "); } b.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR)); b.append(" - "); b.append(error.description); b.append('\n'); if (sourceExcerpt != null) { b.append(sourceExcerpt); b.append('\n'); int charno = error.getCharno(); // padding equal to the excerpt and arrow at the end // charno == sourceExpert.length

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>() means something is missing // at the end of the line if (excerpt.equals(LINE) && 0 <= charno && charno <= sourceExcerpt.length()) { for (int i = 0; i < charno; i++) { char c = sourceExcerpt.charAt(i); if (Character.isWhitespace(c)) { b.append(c); } else { b.append(' '); } } b.append("^\n"); } } return b.toString(); } /** * Formats a region by appending line numbers in front, e.g. * <pre> 9| if (foo) { * 10| alert('bar'); * 11| }</pre> * and return line excerpt without any modification. */ static class LineNumberingFormatter implements ExcerptFormatter { @Override public String formatLine(String line, int lineNumber) { return line; } @Override public String formatRegion(Region region) { if (region == null) { return null; } String code = region.getSourceExcerpt(); if (code.length() == 0) { return null; } // max length of the number display int numberLength = Integer.toString(region.getEndingLineNumber()) .length(); // formatting StringBuilder builder = new StringBuilder(code.length() * 2); int start = 0; int end = code.indexOf('\n', start); int lineNumber = region.getBeginningLineNumber(); while (start >= 0) { // line extraction String line; if (end < 0) { line = code.substring(start); if (line.length() == 0) { return builder.substring(0, builder.length() - 1); } } else { line = code.substring(start, end); } builder.append(" "); // nice spaces for the line number int spaces = numberLength - Integer.toString(lineNumber).length(); builder.append(Strings.repeat(" ", spaces)); builder.append(lineNumber); builder.append("| "); // end & update if (end < 0) { builder.append(line); start = -1; } else { builder.append(line); builder.append('\n'); start = end + 1; end = code.indexOf('\n', start); lineNumber++; } } return builder.toString(); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.deps; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; import java.util.ArrayDeque; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Set; /** * A sorted list of inputs with dependency information. Uses a stable * topological sort to make sure that an input always comes after its * dependencies. * * Also exposes other information about the inputs, like which inputs * do not provide symbols. * * @author nicksantos@google.com (Nick Santos) */ public class SortedDependencies<INPUT extends DependencyInfo> { private final List<INPUT> inputs; // A topologically sorted list of the inputs. private final List<INPUT> sortedList; // A list of all the inputs that do not have provides. private final List<INPUT> noProvides; private final Map<String, INPUT> provideMap = Maps.newHashMap(); public SortedDependencies(List<INPUT> inputs) throws CircularDependencyException { this.inputs = Lists.newArrayList(inputs); noProvides = Lists.newArrayList(); // Collect all symbols provided in these files. for (INPUT input : inputs) { Collection<String> currentProvides = input.getProvides(); if (currentProvides.isEmpty()) { noProvides.add(input); } for (String provide : currentProvides) { provideMap.put(provide, input); } } // Get the direct dependencies. final Multimap<INPUT, INPUT> deps = HashMultimap.create(); for (INPUT input : inputs) { for (String req : input.getRequires()) { INPUT dep = provideMap.get(req); if (dep != null && dep != input) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> deps.put(input, dep); } } } // Sort the inputs by sucking in 0-in-degree nodes until we're done. sortedList = topologicalStableSort(inputs, deps); // The dependency graph of inputs has a cycle iff sortedList is a proper // subset of inputs. Also, it has a cycle iff the subgraph // (inputs - sortedList) has a cycle. It's fairly easy to prove this // by the lemma that a graph has a cycle iff it has a subgraph where // no nodes have out-degree 0. I'll leave the proof of this as an exercise // to the reader. if (sortedList.size() < inputs.size()) { List<INPUT> subGraph = Lists.newArrayList(inputs); subGraph.removeAll(sortedList); throw new CircularDependencyException( cycleToString(findCycle(subGraph, deps))); } } /** * Return the input that gives us the given symbol. * @throws MissingProvideException An exception if there is no * input for this symbol. */ public INPUT getInputProviding(String symbol) throws MissingProvideException { if (provideMap.containsKey(symbol)) { return provideMap.get(symbol); } throw new MissingProvideException(symbol); } /** * Return the input that gives us the given symbol, or null. */ public INPUT maybeGetInputProviding(String symbol) { return provideMap.get(symbol); } /** * Returns the first circular dependency found. Expressed as a list of * items in reverse dependency order (the second element depends on the * first, etc.). */ private List<INPUT> findCycle( List<INPUT> subGraph, Multimap<INPUT, INPUT> deps) { return findCycle(subGraph.get(0), Sets.<INPUT>newHashSet(subGraph), deps, Sets.<INPUT>newHashSet()); } private List<INPUT> findCycle( INPUT current, Set<INPUT> subGraph, Multimap<INPUT, INPUT> deps, Set<INPUT> covered) { if (covered.add(current)) { List<INPUT> cycle = findCycle( findRequireInSubGraphOrFail(current, subGraph), subGraph, deps, covered); // Don't add the input to the list if the cycle has closed already. if (cycle.get(0) != cycle.get(cycle.size() - 1)) { cycle.add(current); } return cycle; } else { // Explicitly use the add() method, to prevent a generics constructor // warning that is dumb. The condition it's protecting is // obscure, and I think people have proposed that it be removed. List<INPUT> cycle = Lists.<INPUT>newArrayList(); cycle.add(current); return cycle; } } private INPUT findRequireInSubGraphOrFail(INPUT input, Set<INPUT> subGraph) { for (String symbol : input.getRequires()) { INPUT candidate = provideMap.get(symbol); if (subGraph.contains(candidate)) { return candidate; } } throw new IllegalStateException("no require found in subgraph"); } /** * @

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>param cycle A cycle in reverse-dependency order. */ private String cycleToString(List<INPUT> cycle) { List<String> symbols = Lists.newArrayList(); for (int i = cycle.size() - 1; i >= 0; i--) { symbols.add(cycle.get(i).getProvides().iterator().next()); } symbols.add(symbols.get(0)); return Joiner.on(" -> ").join(symbols); } public List<INPUT> getSortedList() { return Collections.<INPUT>unmodifiableList(sortedList); } /** * Gets all the dependencies of the given roots. The inputs must be returned * in a stable order. In other words, if A comes before B, and A does not * transitively depend on B, then A must also come before B in the returned * list. */ public List<INPUT> getSortedDependenciesOf(List<INPUT> roots) { return getDependenciesOf(roots, true); } /** * Gets all the dependencies of the given roots. The inputs must be returned * in a stable order. In other words, if A comes before B, and A does not * transitively depend on B, then A must also come before B in the returned * list. * * @param sorted If true, get them in topologically sorted order. If false, * get them in the original order they were passed to the compiler. */ public List<INPUT> getDependenciesOf(List<INPUT> roots, boolean sorted) { Preconditions.checkArgument(inputs.containsAll(roots)); Set<INPUT> included = Sets.newHashSet(); Deque<INPUT> worklist = new ArrayDeque<INPUT>(roots); while (!worklist.isEmpty()) { INPUT current = worklist.pop(); if (included.add(current)) { for (String req : current.getRequires()) { INPUT dep = provideMap.get(req); if (dep != null) { worklist.add(dep); } } } } ImmutableList.Builder<INPUT> builder = ImmutableList.builder(); for (INPUT current : (sorted ? sortedList : inputs)) { if (included.contains(current)) { builder.add(current); } } return builder.build(); } public List<INPUT> getInputsWithoutProvides() { return Collections.<INPUT>unmodifiableList(noProvides); } private static <T> List<T> topologicalStableSort( List<T> items, Multimap<T, T> deps) { if (items.size() == 0) { // Priority queue blows up if we give it a size of 0. Since we need // to special case this either way, just bail out. return Lists.newArrayList(); } final Map<T, Integer> originalIndex = Maps.newHashMap(); for (int i = 0; i < items.size(); i++) { originalIndex.put(items.get(i), i); } PriorityQueue<T> inDegreeZero = new PriorityQueue<T>(items.size(), new Comparator<T>() { @Override public int compare(T a, T b) { return originalIndex.get(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.rhino.Node; import java.util.List; /** * A visitor implementation that enables type substitutions. * * @author johnlenz@google.com (John Lenz) */ public class ModificationVisitor implements Visitor<JSType> { private final JSTypeRegistry registry; public ModificationVisitor(JSTypeRegistry registry) { this.registry = registry; } @Override public JSType caseNoType() { return getNativeType(JSTypeNative.NO_TYPE); } @Override public JSType caseEnumElementType(EnumElementType type) { return type; } @Override public JSType caseAllType() { return getNativeType(JSTypeNative.ALL_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(JSTypeNative.BOOLEAN_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(JSTypeNative.NO_OBJECT_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { if (isNativeFunctionType(type)) { return type; } // TODO(johnlenz): remove this simplifying assumption... if (!type.isOrdinaryFunction

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>()) { return type; } boolean changed = false; JSType beforeThis = type.getTypeOfThis(); JSType afterThis = coerseToThisType(beforeThis.visit(this)); if (beforeThis != afterThis) { changed = true; } JSType beforeReturn = type.getReturnType(); JSType afterReturn = beforeReturn.visit(this); if (beforeReturn != afterReturn) { changed = true; } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(registry); for (Node paramNode : type.getParameters()) { JSType beforeParamType = paramNode.getJSType(); JSType afterParamType = beforeParamType.visit(this); if (beforeParamType != afterParamType) { changed = true; } if (paramNode.isOptionalArg()) { paramBuilder.addOptionalParams(afterParamType); } else if (paramNode.isVarArgs()) { paramBuilder.addVarArgs(afterParamType); } else { paramBuilder.addRequiredParams(afterParamType); } } if (changed) { FunctionBuilder builder = new FunctionBuilder(registry); builder.withParams(paramBuilder); builder.withReturnType(afterReturn); builder.withTypeOfThis(afterThis); builder.withTemplateKeys(type.getTemplateTypeMap().getUnfilledTemplateKeys()); return builder.build(); } return type; } private JSType coerseToThisType(JSType type) { return type != null ? type : registry.getNativeObjectType( JSTypeNative.UNKNOWN_TYPE); } @Override public JSType caseObjectType(ObjectType objType) { return objType; } @Override public JSType caseTemplatizedType(TemplatizedType type) { boolean changed = false; ObjectType beforeBaseType = type.getReferencedType(); ObjectType afterBaseType = ObjectType.cast(beforeBaseType.visit(this)); if (beforeBaseType != afterBaseType) { changed = true; } ImmutableList.Builder<JSType> builder = ImmutableList.builder(); for (JSType beforeTemplateType : type.getTemplateTypes()) { JSType afterTemplateType = beforeTemplateType.visit(this); if (beforeTemplateType != afterTemplateType) { changed = true; } builder.add(afterTemplateType); } if (changed) { type = registry.createTemplatizedType(afterBaseType, builder.build()); } return type; } @Override public JSType caseUnknownType() { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } @Override public JSType caseNullType() { return getNativeType(JSTypeNative.NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(JSTypeNative.NUMBER_TYPE); } @Override public JSType caseStringType() { return getNativeType(JSTypeNative.STRING_TYPE); } @Override public JSType caseVoidType() { return getNativeType(JSTypeNative.VOID_TYPE); } @Override public JSType caseUnionType(UnionType type) { boolean changed = false; List<JSType>

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> results = Lists.newArrayList(); for (JSType alternative : type.getAlternates()) { JSType replacement = alternative.visit(this); if (replacement != alternative) { changed = true; } results.add(replacement); } if (changed) { UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (JSType alternate : results) { builder.addAlternate(alternate); } return builder.build(); // maybe not a union } return type; } @Override public JSType caseTemplateType(TemplateType type) { return type; } private JSType getNativeType(JSTypeNative nativeType) { return registry.getNativeType(nativeType); } private boolean isNativeFunctionType(FunctionType type) { return type.isNativeObjectType(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; /** * A namespace type is a reference to a particular object. * * This is generally useful when you have a particular object * in mind, but need to give it a name when it's passed to a function. * * For example, * <code> * /** @namespace / * var jQuery = {}; * * /** @return {jQuery.} / * jQuery.get = function () { * return jQuery // for easy chaining * } * </code> * * @see https://docs.google.com/document/d/1r37CJ6ZW0zk28IMn1Tu8UKKjs2WcJ-6dNEb3om7FoHQ * @author nicholas.j.santos@gmail.com (Nick Santos) */ class NamespaceType extends NamedType { private static final long serialVersionUID = 1L; /** * Create a namespace type based on the reference. */ NamespaceType(JSTypeRegistry registry, String reference, String sourceName, int lineno, int charno) { super(registry, reference, sourceName, lineno, charno); } /** * Resolve the referenced type within the enclosing scope. */ @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> enclosing) { warning(t

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ReferenceCollectingCallback.BasicBlock; import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior; import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Checks variables to see if they are referenced before their declaration, or * if they are redeclared in a way that is suspicious (i.e. not dictated by * control structures). This is a more aggressive version of {@link VarCheck}, * but it lacks the cross-module checks. * * @author kushal@google.com (Kushal Dave) */ class VariableReferenceCheck implements HotSwapCompilerPass { static final DiagnosticType UNDECLARED_REFERENCE = DiagnosticType.warning( "JSC_REFERENCE_BEFORE_DECLARE", "Variable referenced before declaration: {0}"); static final DiagnosticType REDECLARED_VARIABLE = DiagnosticType.warning( "JSC_REDECLARED_VARIABLE", "Redeclared variable: {0}"); static final DiagnosticType AMBIGUOUS_FUNCTION_DECL = DiagnosticType.disabled("AMBIGUOUS_FUNCTION_DECL", "Ambiguous use of a named function: {0}."); private final AbstractCompiler compiler; private final CheckLevel checkLevel; // NOTE(nicksantos): It's a lot faster to use a shared Set that // we clear after each method call, because the Set never gets too big. private final Set<BasicBlock> blocksWithDeclarations = Sets.newHashSet(); public VariableReferenceCheck(AbstractCompiler compiler, CheckLevel checkLevel) { this.compiler = compiler; this.checkLevel = checkLevel; } @Override public void process(Node externs, Node root) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback( compiler, new ReferenceCheckingBehavior()); callback.process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback( compiler, new ReferenceCheckingBehavior()); callback.hotSwapScript(scriptRoot, originalRoot); } /** *

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Behavior that checks variables for redeclaration or early references * just after they go out of scope. */ private class ReferenceCheckingBehavior implements Behavior { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) { // TODO(bashir) In hot-swap version this means that for global scope we // only go through all global variables accessed in the modified file not // all global variables. This should be fixed. // Check all vars after finishing a scope for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); checkVar(v, referenceMap.getReferences(v).references); } } /** * If the variable is declared more than once in a basic block, generate a * warning. Also check if a variable is used in a given scope before it is * declared, which suggest a likely error. Relies on the fact that * references is in parse-tree order. */ private void checkVar(Var v, List<Reference> references) { blocksWithDeclarations.clear(); boolean isDeclaredInScope = false; boolean isUnhoistedNamedFunction = false; Reference hoistedFn = null; // Look for hoisted functions. for (Reference reference : references) { if (reference.isHoistedFunction()) { blocksWithDeclarations.add(reference.getBasicBlock()); isDeclaredInScope = true; hoistedFn = reference; break; } else if (NodeUtil.isFunctionDeclaration( reference.getNode().getParent())) { isUnhoistedNamedFunction = true; } } for (Reference reference : references) { if (reference == hoistedFn) { continue; } BasicBlock basicBlock = reference.getBasicBlock(); boolean isDeclaration = reference.isDeclaration(); boolean allowDupe = VarCheck.hasDuplicateDeclarationSuppression( reference.getNode(), v); if (isDeclaration && !allowDupe) { // Look through all the declarations we've found so far, and // check if any of them are before this block. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (declaredBlock.provablyExecutesBefore(basicBlock)) { // TODO(johnlenz): Fix AST generating clients that so they would // have property StaticSourceFile attached at each node. Or // better yet, make sure the generated code never violates // the requirement to pass aggressive var check! String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), checkLevel, REDECLARED_VARIABLE, v.name)); break; } } } if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) { // Only allow an unhoisted named function to be used within the // block it is declared. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (!declaredBlock.provablyExecutesBefore(basicBlock)) { String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), AMBIGUOUS_FUNCTION_DECL, v.name)); break; }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> final boolean nativeType; // NOTE(nicksantos): The implicit prototype can change over time. // Modeling this is a bear. Always call getImplicitPrototype(), because // some subclasses override this to do special resolution handling. private ObjectType implicitPrototypeFallback; // If this is a function prototype, then this is the owner. // A PrototypeObjectType can only be the prototype of one function. If we try // to do this for multiple functions, then we'll have to create a new one. private FunctionType ownerFunction = null; // Whether the toString representation of this should be pretty-printed, // by printing all properties. private boolean prettyPrint = false; private static final int MAX_PRETTY_PRINTED_PROPERTIES = 4; /** * Creates an object type. * * @param className the name of the class. May be {@code null} to * denote an anonymous class. * * @param implicitPrototype the implicit prototype * (a.k.a. {@code [[Prototype]]}) as defined by ECMA-262. If the * implicit prototype is {@code null} the implicit prototype will be * set to the {@link JSTypeNative#OBJECT_TYPE}. */ PrototypeObjectType(JSTypeRegistry registry, String className, ObjectType implicitPrototype) { this(registry, className, implicitPrototype, false, null); } /** * Creates an object type, allowing specification of the implicit prototype, * whether the object is native, and any templatized types. */ PrototypeObjectType(JSTypeRegistry registry, String className, ObjectType implicitPrototype, boolean nativeType, TemplateTypeMap templateTypeMap) { super(registry, templateTypeMap); this.properties = new PropertyMap(); this.properties.setParentSource(this); this.className = className; this.nativeType = nativeType; if (nativeType || implicitPrototype != null) { setImplicitPrototype(implicitPrototype); } else { setImplicitPrototype( registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE)); } } @Override PropertyMap getPropertyMap() { return properties; } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { if (hasOwnDeclaredProperty(name)) { return false; } Property newProp = new Property( name, type, inferred, propertyNode); properties.putProperty(name, newProp); return true; } @Override public boolean removeProperty(String name) { return properties.removeProperty(name); } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { if (info != null) { if (properties.getOwnProperty(propertyName) == null) { // If docInfo was attached, but the type of the property // was not defined anywhere, then we consider this an explicit // declaration of the property. defineInferredProperty(propertyName, getPropertyType(propertyName), null); } // The prototype property is not represented as a normal Property. // We probably don't want to attach any JSDoc to it anyway. Property property = properties.getOwnProperty(propertyName); if (property != null) { property.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>setJSDocInfo(info); } } } @Override public boolean matchesNumberContext() { return isNumberObjectType() || isDateType() || isBooleanObjectType() || isStringObjectType() || hasOverridenNativeProperty("valueOf"); } @Override public boolean matchesStringContext() { return isTheObjectType() || isStringObjectType() || isDateType() || isRegexpType() || isArrayType() || isNumberObjectType() || isBooleanObjectType() || hasOverridenNativeProperty("toString"); } /** * Given the name of a native object property, checks whether the property is * present on the object and different from the native one. */ private boolean hasOverridenNativeProperty(String propertyName) { if (isNativeObjectType()) { return false; } JSType propertyType = getPropertyType(propertyName); ObjectType nativeType = isFunctionType() ? registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE) : registry.getNativeObjectType(JSTypeNative.OBJECT_PROTOTYPE); JSType nativePropertyType = nativeType.getPropertyType(propertyName); return propertyType != nativePropertyType; } @Override public JSType unboxesTo() { if (isStringObjectType()) { return getNativeType(JSTypeNative.STRING_TYPE); } else if (isBooleanObjectType()) { return getNativeType(JSTypeNative.BOOLEAN_TYPE); } else if (isNumberObjectType()) { return getNativeType(JSTypeNative.NUMBER_TYPE); } else { return super.unboxesTo(); } } @Override public boolean matchesObjectContext() { return true; } @Override public boolean canBeCalled() { return isRegexpType(); } @Override String toStringHelper(boolean forAnnotations) { if (hasReferenceName()) { return getReferenceName(); } else if (prettyPrint) { // Don't pretty print recursively. prettyPrint = false; // Use a tree set so that the properties are sorted. Set<String> propertyNames = Sets.newTreeSet(); for (ObjectType current = this; current != null && !current.isNativeObjectType() && propertyNames.size() <= MAX_PRETTY_PRINTED_PROPERTIES; current = current.getImplicitPrototype()) { propertyNames.addAll(current.getOwnPropertyNames()); } StringBuilder sb = new StringBuilder(); sb.append("{"); int i = 0; for (String property : propertyNames) { if (i > 0) { sb.append(", "); } sb.append(property); sb.append(": "); sb.append(getPropertyType(property).toStringHelper(forAnnotations)); ++i; if (!forAnnotations && i == MAX_PRETTY_PRINTED_PROPERTIES) { sb.append(", ..."); break; } } sb.append("}"); prettyPrint = true; return sb.toString(); } else { return forAnnotations ? "?" : "{...}"; } } void setPrettyPrint(boolean prettyPrint) { this.prettyPrint = prettyPrint; } boolean isPrettyPrint() { return prettyPrint; } @Override public FunctionType getConstructor() {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> return null; } @Override public ObjectType getImplicitPrototype() { return implicitPrototypeFallback; } /** * This should only be reset on the FunctionPrototypeType, only to fix an * incorrectly established prototype chain due to the user having a mismatch * in super class declaration, and only before properties on that type are * processed. */ final void setImplicitPrototype(ObjectType implicitPrototype) { checkState(!hasCachedValues()); this.implicitPrototypeFallback = implicitPrototype; } @Override public String getReferenceName() { if (className != null) { return className; } else if (ownerFunction != null) { return ownerFunction.getReferenceName() + ".prototype"; } else { return null; } } @Override public boolean hasReferenceName() { return className != null || ownerFunction != null; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } // Union types if (that.isUnionType()) { // The static {@code JSType.isSubtype} check already decomposed // union types, so we don't need to check those again. return false; } // record types if (that.isRecordType()) { return RecordType.isSubtype(this, that.toMaybeRecordType()); } // Interfaces // Find all the interfaces implemented by this class and compare each one // to the interface instance. ObjectType thatObj = that.toObjectType(); FunctionType thatCtor = thatObj == null ? null : thatObj.getConstructor(); if (getConstructor() != null && getConstructor().isInterface()) { for (ObjectType thisInterface : getCtorExtendedInterfaces()) { if (thisInterface.isSubtype(that)) { return true; } } } else if (thatCtor != null && thatCtor.isInterface()) { Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces(); for (ObjectType thisInterface : thisInterfaces) { if (thisInterface.isSubtype(that)) { return true; } } } // other prototype based objects if (isUnknownType() || implicitPrototypeChainIsUnknown()) { // If unsure, say 'yes', to avoid spurious warnings. // TODO(user): resolve the prototype chain completely in all cases, // to avoid guessing. return true; } return thatObj != null && isImplicitPrototype(thatObj); } private boolean implicitPrototypeChainIsUnknown() { ObjectType p = getImplicitPrototype(); while (p != null) { if (p.isUnknownType()) { return true; } p = p.getImplicitPrototype(); } return false; } @Override public boolean hasCachedValues() { return super.hasCachedValues(); } /** Whether this is a built-in object. */ @Override public boolean isNativeObjectType() { return nativeType; } @Override void setOwnerFunction(FunctionType type) { Preconditions.checkState(ownerFunction == null || type == null); ownerFunction = type; } @Override public FunctionType getOwnerFunction() { return ownerFunction; } @Override public Iterable<

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>ObjectType> getCtorImplementedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getImplementedInterfaces() : ImmutableList.<ObjectType>of(); } @Override public Iterable<ObjectType> getCtorExtendedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getExtendedInterfaces() : ImmutableList.<ObjectType>of(); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { implicitPrototypeFallback = (ObjectType) implicitPrototype.resolve(t, scope); } for (Property prop : properties.values()) { prop.setType(safeResolve(prop.getType(), t, scope)); } return this; } @Override public void matchConstraint(JSType constraint) { // We only want to match constraints on anonymous types. if (hasReferenceName()) { return; } // Handle the case where the constraint object is a record type. // // param constraint {{prop: (number|undefined)}} // function f(constraint) {} // f({}); // // We want to modify the object literal to match the constraint, by // taking any each property on the record and trying to match // properties on this object. if (constraint.isRecordType()) { matchRecordTypeConstraint(constraint.toObjectType()); } else if (constraint.isUnionType()) { for (JSType alt : constraint.toMaybeUnionType().getAlternates()) { if (alt.isRecordType()) { matchRecordTypeConstraint(alt.toObjectType()); } } } } public void matchRecordTypeConstraint(ObjectType constraintObj) { for (String prop : constraintObj.getOwnPropertyNames()) { JSType propType = constraintObj.getPropertyType(prop); if (!isPropertyTypeDeclared(prop)) { JSType typeToInfer = propType; if (!hasProperty(prop)) { typeToInfer = getNativeType(JSTypeNative.VOID_TYPE) .getLeastSupertype(propType); } defineInferredProperty(prop, typeToInfer, null); } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> import java.util.concurrent.ThreadFactory; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; /** * Compiler (and the other classes in this package) does the following: * <ul> * <li>parses JS code * <li>checks for undefined variables * <li>performs optimizations such as constant folding and constants inlining * <li>renames variables (to short names) * <li>outputs compact JavaScript code * </ul> * * External variables are declared in 'externs' files. For instance, the file * may include definitions for global javascript/browser objects such as * window, document. * */ public class Compiler extends AbstractCompiler { static final String SINGLETON_MODULE_NAME = "[singleton]"; static final DiagnosticType MODULE_DEPENDENCY_ERROR = DiagnosticType.error("JSC_MODULE_DEPENDENCY_ERROR", "Bad dependency: {0} -> {1}. " + "Modules must be listed in dependency order."); static final DiagnosticType MISSING_ENTRY_ERROR = DiagnosticType.error( "JSC_MISSING_ENTRY_ERROR", "required entry point \"{0}\" never provided"); static final DiagnosticType MISSING_MODULE_ERROR = DiagnosticType.error( "JSC_MISSING_ENTRY_ERROR", "unknown module \"{0}\" specified in entry point spec"); // Used in PerformanceTracker static final String PARSING_PASS_NAME = "parseInputs"; private static final String CONFIG_RESOURCE = "com.google.javascript.jscomp.parsing.ParserConfig"; CompilerOptions options = null; private PassConfig passes = null; // The externs inputs private List<CompilerInput> externs; // The JS source modules private List<JSModule> modules; // The graph of the JS source modules. Must be null if there are less than // 2 modules, because we use this as a signal for which passes to run. private JSModuleGraph moduleGraph; // The JS source inputs private List<CompilerInput> inputs; // error manager to which error management is delegated private ErrorManager errorManager; // Warnings guard for filtering warnings. private WarningsGuard warningsGuard; // Compile-time injected libraries. The node points to the last node of // the library, so code can be inserted after. private final Map<String, Node> injectedLibraries = Maps.newLinkedHashMap(); // Parse tree root nodes Node externsRoot; Node jsRoot; Node externAndJsRoot; private Map<InputId, CompilerInput> inputsById; /** The source code map */ private SourceMap sourceMap; /** The externs created from the exports. */ private String externExports = null; /** * Ids for function inlining so that each declared name remains * unique. */ private int uniqueNameId = 0; /** * Whether to assume there are references to the RegExp Global object * properties. */ private boolean hasRegExpGlobalReferences = true; /** The function information map */ private FunctionInformationMap functionInformationMap; /** Debugging information */ private final StringBuilder debugLog = new StringBuilder(); /** Detect

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>s Google-specific coding conventions. */ CodingConvention defaultCodingConvention = new ClosureCodingConvention(); private JSTypeRegistry typeRegistry; private Config parserConfig = null; private ReverseAbstractInterpreter abstractInterpreter; private TypeValidator typeValidator; // The compiler can ask phaseOptimizer for things like which pass is currently // running, or which functions have been changed by optimizations private PhaseOptimizer phaseOptimizer = null; public PerformanceTracker tracker; // The oldErrorReporter exists so we can get errors from the JSTypeRegistry. private final com.google.javascript.rhino.ErrorReporter oldErrorReporter = RhinoErrorReporter.forOldRhino(this); // This error reporter gets the messages from the current Rhino parser. private final ErrorReporter defaultErrorReporter = RhinoErrorReporter.forNewRhino(this); /** Error strings used for reporting JSErrors */ public static final DiagnosticType OPTIMIZE_LOOP_ERROR = DiagnosticType.error( "JSC_OPTIMIZE_LOOP_ERROR", "Exceeded max number of optimization iterations: {0}"); public static final DiagnosticType MOTION_ITERATIONS_ERROR = DiagnosticType.error("JSC_OPTIMIZE_LOOP_ERROR", "Exceeded max number of code motion iterations: {0}"); // We use many recursive algorithms that use O(d) memory in the depth // of the tree. private static final long COMPILER_STACK_SIZE = (1 << 21); // About 2MB /** * Under JRE 1.6, the JS Compiler overflows the stack when running on some * large or complex JS code. When threads are available, we run all compile * jobs on a separate thread with a larger stack. * * That way, we don't have to increase the stack size for *every* thread * (which is what -Xss does). */ private static final ExecutorService compilerExecutor = Executors.newCachedThreadPool(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(null, r, "jscompiler", COMPILER_STACK_SIZE); } }); /** * Use a dedicated compiler thread per Compiler instance. */ private Thread compilerThread = null; /** Whether to use threads. */ private boolean useThreads = true; /** * Logger for the whole com.google.javascript.jscomp domain - * setting configuration for this logger affects all loggers * in other classes within the compiler. */ private static final Logger logger = Logger.getLogger("com.google.javascript.jscomp"); private final PrintStream outStream; private GlobalVarReferenceMap globalRefMap = null; private volatile double progress = 0.0; private String lastPassName; /** * Creates a Compiler that reports errors and warnings to its logger. */ public Compiler() { this((PrintStream) null); } /** * Creates a Compiler that reports errors and warnings to an output stream. */ public Compiler(PrintStream stream) { addChangeHandler(recentChange); outStream = stream; } /** * Creates a Compiler that uses a custom error manager. */ public Compiler(ErrorManager errorManager) { this(); setErrorManager(errorManager); } /**

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>_LIST_ERROR", "At least one module must be provided"); private static final DiagnosticType EMPTY_ROOT_MODULE_ERROR = DiagnosticType.error("JSC_EMPTY_ROOT_MODULE_ERROR", "Root module '{0}' must contain at least one source code input"); /** * Verifies that at least one module has been provided and that the first one * has at least one source code input. */ private void checkFirstModule(List<JSModule> modules) { if (modules.isEmpty()) { report(JSError.make(EMPTY_MODULE_LIST_ERROR)); } else if (modules.get(0).getInputs().isEmpty() && modules.size() > 1) { // The root module may only be empty if there is exactly 1 module. report(JSError.make(EMPTY_ROOT_MODULE_ERROR, modules.get(0).getName())); } } /** * Empty modules get an empty "fill" file, so that we can move code into * an empty module. */ static String createFillFileName(String moduleName) { return "[" + moduleName + "]"; } /** * Fill any empty modules with a place holder file. It makes any cross module * motion easier. */ private static void fillEmptyModules(List<JSModule> modules) { for (JSModule module : modules) { if (module.getInputs().isEmpty()) { module.add(SourceFile.fromCode( createFillFileName(module.getName()), "")); } } } /** * Rebuilds the internal list of inputs by iterating over all modules. * This is necessary if inputs have been added to or removed from a module * after the {@link #init(List, List, CompilerOptions)} call. */ public void rebuildInputsFromModules() { inputs = getAllInputsFromModules(modules); initInputsByIdMap(); } /** * Builds a single list of all module inputs. Verifies that it contains no * duplicates. */ private static List<CompilerInput> getAllInputsFromModules( List<JSModule> modules) { List<CompilerInput> inputs = Lists.newArrayList(); Map<String, JSModule> inputMap = Maps.newHashMap(); for (JSModule module : modules) { for (CompilerInput input : module.getInputs()) { String inputName = input.getName(); // NOTE(nicksantos): If an input is in more than one module, // it will show up twice in the inputs list, and then we // will get an error down the line. inputs.add(input); inputMap.put(inputName, module); } } return inputs; } static final DiagnosticType DUPLICATE_INPUT = DiagnosticType.error("JSC_DUPLICATE_INPUT", "Duplicate input: {0}"); static final DiagnosticType DUPLICATE_EXTERN_INPUT = DiagnosticType.error("JSC_DUPLICATE_EXTERN_INPUT", "Duplicate extern input: {0}"); /** * Creates a map to make looking up an input by name fast. Also checks for * duplicate inputs. */ void initInputsByIdMap() { inputsById = new HashMap<InputId, CompilerInput>(); for (CompilerInput input : extern

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>acer t = newTracer("generateReport"); errorManager.generateReport(); stopTracer(t, "generateReport"); } } private Result compile() { return runInCompilerThread(new Callable<Result>() { @Override public Result call() throws Exception { compileInternal(); return getResult(); } }); } /** * Disable threads. This is for clients that run on AppEngine and * don't have threads. */ public void disableThreads() { useThreads = false; } @SuppressWarnings("unchecked") <T> T runInCompilerThread(final Callable<T> callable) { T result = null; final Throwable[] exception = new Throwable[1]; Preconditions.checkState( compilerThread == null || compilerThread == Thread.currentThread(), "Please do not share the Compiler across threads"); // If the compiler thread is available, use it. if (useThreads && compilerThread == null) { try { final boolean dumpTraceReport = options != null && options.tracer.isOn(); Callable<T> bootCompilerThread = new Callable<T>() { @Override public T call() { try { compilerThread = Thread.currentThread(); if (dumpTraceReport) { Tracer.initCurrentThreadTrace(); } return callable.call(); } catch (Throwable e) { exception[0] = e; } finally { compilerThread = null; if (dumpTraceReport) { Tracer.logCurrentThreadTrace(); tracker.outputTracerReport(outStream == null ? System.out : outStream); } Tracer.clearCurrentThreadTrace(); } return null; } }; result = compilerExecutor.submit(bootCompilerThread).get(); } catch (InterruptedException e) { throw Throwables.propagate(e); } catch (ExecutionException e) { throw Throwables.propagate(e); } } else { try { result = callable.call(); } catch (Exception e) { exception[0] = e; } finally { Tracer.clearCurrentThreadTrace(); } } // Pass on any exception caught by the runnable object. if (exception[0] != null) { throw new RuntimeException(exception[0]); } return result; } private void compileInternal() { setProgress(0.0, null); parse(); // 15 percent of the work is assumed to be for parsing (based on some // minimal analysis on big JS projects, of course this depends on options) setProgress(0.15, "parse"); if (hasErrors()) { return; } if (!precheck()) { return; } if (options.nameAnonymousFunctionsOnly) { // TODO(nicksantos): Move this into an instrument() phase maybe? check(); return; } if (!options.skipAllPasses) { check(); if (hasErrors()) { return; } // IDE-mode is defined to stop here, before the heavy rewriting begins. if (!options.ideMode) { optimize(); } } if (options.recordFunctionInformation) { recordFunctionInformation(); } if (options.devMode == Dev

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>this); process(pass); externExports = pass.getGeneratedExterns(); endPass(); } @Override void process(CompilerPass p) { p.process(externsRoot, jsRoot); } private final PassFactory sanityCheck = new PassFactory("sanityCheck", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new SanityCheck(compiler); } }; private void maybeSanityCheck() { if (options.devMode == DevMode.EVERY_PASS) { runSanityCheck(); } } private void runSanityCheck() { sanityCheck.create(this).process(externsRoot, jsRoot); } /** * Removes try/catch/finally statements for easier debugging. */ void removeTryCatchFinally() { logger.fine("Remove try/catch/finally"); startPass("removeTryCatchFinally"); RemoveTryCatch r = new RemoveTryCatch(this); process(r); endPass(); } /** * Strips code for smaller compiled code. This is useful for removing debug * statements to prevent leaking them publicly. */ void stripCode(Set<String> stripTypes, Set<String> stripNameSuffixes, Set<String> stripTypePrefixes, Set<String> stripNamePrefixes) { logger.fine("Strip code"); startPass("stripCode"); StripCode r = new StripCode(this, stripTypes, stripNameSuffixes, stripTypePrefixes, stripNamePrefixes); if (options.getTweakProcessing().shouldStrip()) { r.enableTweakStripping(); } process(r); endPass(); } /** * Runs custom passes that are designated to run at a particular time. */ private void runCustomPasses(CustomPassExecutionTime executionTime) { if (options.customPasses != null) { Tracer t = newTracer("runCustomPasses"); try { for (CompilerPass p : options.customPasses.get(executionTime)) { process(p); } } finally { stopTracer(t, "runCustomPasses"); } } } private Tracer currentTracer = null; private String currentPassName = null; /** * Marks the beginning of a pass. */ void startPass(String passName) { Preconditions.checkState(currentTracer == null); currentPassName = passName; currentTracer = newTracer(passName); } /** * Marks the end of a pass. */ void endPass() { Preconditions.checkState(currentTracer != null, "Tracer should not be null at the end of a pass."); stopTracer(currentTracer, currentPassName); currentPassName = null; currentTracer = null; maybeSanityCheck(); } /** * Returns a new tracer for the given pass name. */ Tracer newTracer(String passName) { String comment = passName + (recentChange.hasCodeChanged() ? " on recently changed AST" : ""); if (options.tracer.isOn()) { tracker.recordPassStart(passName, true); } return new Tracer("Compiler", comment);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } void stopTracer(Tracer t, String passName) { long result = t.stop(); if (options.tracer.isOn()) { tracker.recordPassStop(passName, result); } } /** * Returns the result of the compilation. */ public Result getResult() { PassConfig.State state = getPassConfig().getIntermediateState(); return new Result(getErrors(), getWarnings(), debugLog.toString(), state.variableMap, state.propertyMap, state.anonymousFunctionNameMap, state.stringMap, functionInformationMap, sourceMap, externExports, state.cssNames, state.idGeneratorMap); } /** * Returns an array constructed from errors + temporary warnings. */ public JSError[] getMessages() { return getErrors(); } /** * Returns the array of errors (never null). */ public JSError[] getErrors() { return errorManager.getErrors(); } /** * Returns the array of warnings (never null). */ public JSError[] getWarnings() { return errorManager.getWarnings(); } @Override public Node getRoot() { return externAndJsRoot; } /** * Creates a new id for making unique names. */ private int nextUniqueNameId() { return uniqueNameId++; } /** * Resets the unique name id counter */ @VisibleForTesting void resetUniqueNameId() { uniqueNameId = 0; } @Override Supplier<String> getUniqueNameIdSupplier() { final Compiler self = this; return new Supplier<String>() { @Override public String get() { return String.valueOf(self.nextUniqueNameId()); } }; } @Override boolean areNodesEqualForInlining(Node n1, Node n2) { if (options.ambiguateProperties || options.disambiguateProperties) { // The type based optimizations require that type information is preserved // during other optimizations. return n1.isEquivalentToTyped(n2); } else { return n1.isEquivalentTo(n2); } } //------------------------------------------------------------------------ // Inputs //------------------------------------------------------------------------ // TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler // interface, and which ones should always be injected. @Override public CompilerInput getInput(InputId id) { return inputsById.get(id); } /** * Removes an input file from AST. * @param id The id of the input to be removed. */ protected void removeExternInput(InputId id) { CompilerInput input = getInput(id); if (input == null) { return; } Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName()); inputsById.remove(id); externs.remove(input); Node root = input.getAstRoot(this); if (root != null) { root.detachFromParent(); } } @Override public CompilerInput newExternInput(String name) { SourceAst ast = new SyntheticAst(name); if (inputsById.containsKey(ast.getInputId()))

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>newRoot); CompilerInput newInput = new CompilerInput(ast); // TODO(tylerg): handle this for multiple modules at some point. if (moduleGraph == null && !modules.isEmpty()) { // singleton module modules.get(0).add(newInput); } putCompilerInput(ast.getInputId(), newInput); return true; } @Override JSModuleGraph getModuleGraph() { return moduleGraph; } /** * Gets a module graph. This will always return a module graph, even * in the degenerate case when there's only one module. */ JSModuleGraph getDegenerateModuleGraph() { return moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph; } @Override public JSTypeRegistry getTypeRegistry() { if (typeRegistry == null) { typeRegistry = new JSTypeRegistry(oldErrorReporter, options.looseTypes); } return typeRegistry; } @Override public MemoizedScopeCreator getTypedScopeCreator() { return getPassConfig().getTypedScopeCreator(); } @SuppressWarnings("unchecked") DefaultPassConfig ensureDefaultPassConfig() { PassConfig passes = getPassConfig().getBasePassConfig(); Preconditions.checkState(passes instanceof DefaultPassConfig, "PassConfigs must eventually delegate to the DefaultPassConfig"); return (DefaultPassConfig) passes; } public SymbolTable buildKnownSymbolTable() { SymbolTable symbolTable = new SymbolTable(getTypeRegistry()); MemoizedScopeCreator typedScopeCreator = getTypedScopeCreator(); if (typedScopeCreator != null) { symbolTable.addScopes(typedScopeCreator.getAllMemoizedScopes()); symbolTable.addSymbolsFrom(typedScopeCreator); } else { symbolTable.findScopes(this, externsRoot, jsRoot); } GlobalNamespace globalNamespace = ensureDefaultPassConfig().getGlobalNamespace(); if (globalNamespace != null) { symbolTable.addSymbolsFrom(globalNamespace); } ReferenceCollectingCallback refCollector = new ReferenceCollectingCallback( this, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); NodeTraversal.traverse(this, getRoot(), refCollector); symbolTable.addSymbolsFrom(refCollector); PreprocessorSymbolTable preprocessorSymbolTable = ensureDefaultPassConfig().getPreprocessorSymbolTable(); if (preprocessorSymbolTable != null) { symbolTable.addSymbolsFrom(preprocessorSymbolTable); } symbolTable.fillNamespaceReferences(); symbolTable.fillPropertyScopes(); symbolTable.fillThisReferences(this, externsRoot, jsRoot); symbolTable.fillPropertySymbols(this, externsRoot, jsRoot); symbolTable.fillJSDocInfo(this, externsRoot, jsRoot); return symbolTable; } @Override public Scope getTopScope() { return getPassConfig().getTopScope(); } @Override public ReverseAbstractInterpreter getReverseAbstractInterpreter() { if (abstractInterpreter == null) { ChainableReverseAbstractInterpreter interpreter = new SemanticReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()); if (options.closurePass) { interpreter = new ClosureReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()) .append(interpreter).getFirst(); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> abstractInterpreter = interpreter; } return abstractInterpreter; } @Override TypeValidator getTypeValidator() { if (typeValidator == null) { typeValidator = new TypeValidator(this); } return typeValidator; } //------------------------------------------------------------------------ // Parsing //------------------------------------------------------------------------ /** * Parses the externs and main inputs. * * @return A synthetic root node whose two children are the externs root * and the main root */ Node parseInputs() { boolean devMode = options.devMode != DevMode.OFF; // If old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main JS sources. jsRoot = IR.block(); jsRoot.setIsSyntheticBlock(true); externsRoot = IR.block(); externsRoot.setIsSyntheticBlock(true); externAndJsRoot = IR.block(externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); if (options.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options.tracer); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer(PARSING_PASS_NAME); try { // Parse externs sources. for (CompilerInput input : externs) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } externsRoot.addChildToBack(n); } // Modules inferred in ProcessCommonJS pass. if (options.transformAMDToCJSModules || options.processCommonJSModules) { processAMDAndCommonJSModules(); } hoistExterns(externsRoot); // Check if the sources need to be re-ordered. boolean staleInputs = false; if (options.dependencyOptions.needsManagement()) { for (CompilerInput input : inputs) { // Forward-declare all the provided types, so that they // are not flagged even if they are dropped from the process. for (String provide : input.getProvides()) { getTypeRegistry().forwardDeclareType(provide); } } try { inputs = (moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph) .manageDependencies(options.dependencyOptions, inputs); staleInputs = true; } catch (CircularDependencyException e) { report(JSError.make( JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); } catch (MissingProvideException e) { report(JSError.make( MISSING_ENTRY_ERROR, e.getMessage())); } catch (JSModuleGraph.MissingModuleException e) { report(JSError.make( MISSING_MODULE_ERROR, e.getMessage())); } // If in IDE mode, we ignore the error and keep going. if (hasErrors()) { return null; } } hoistNoCompileFiles(); if (staleInputs) { repartitionInputs(); } // Build the AST. for (CompilerInput

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> can * process and creates JSModules and the corresponding dependency tree * on the way. */ void processAMDAndCommonJSModules() { Map<String, JSModule> modulesByName = Maps.newLinkedHashMap(); Map<CompilerInput, JSModule> modulesByInput = Maps.newLinkedHashMap(); // TODO(nicksantos): Refactor module dependency resolution to work nicely // with multiple ways to express dependencies. Directly support JSModules // that are equivalent to a signal file and which express their deps // directly in the source. for (CompilerInput input : inputs) { input.setCompiler(this); Node root = input.getAstRoot(this); if (root == null) { continue; } if (options.transformAMDToCJSModules) { new TransformAMDToCJSModule(this).process(null, root); } if (options.processCommonJSModules) { ProcessCommonJSModules cjs = new ProcessCommonJSModules(this, options.commonJSModulePathPrefix); cjs.process(null, root); JSModule m = cjs.getModule(); if (m != null) { modulesByName.put(m.getName(), m); modulesByInput.put(input, m); } } } if (options.processCommonJSModules) { List<JSModule> modules = Lists.newArrayList(modulesByName.values()); if (!modules.isEmpty()) { this.modules = modules; this.moduleGraph = new JSModuleGraph(this.modules); } for (JSModule module : modules) { for (CompilerInput input : module.getInputs()) { for (String require : input.getRequires()) { JSModule dependency = modulesByName.get(require); if (dependency == null) { report(JSError.make(MISSING_ENTRY_ERROR, require)); } else { module.addDependency(dependency); } } } } try { modules = Lists.newArrayList(); for (CompilerInput input : this.moduleGraph.manageDependencies( options.dependencyOptions, inputs)) { modules.add(modulesByInput.get(input)); } JSModule root = new JSModule("root"); for (JSModule m : modules) { m.addDependency(root); } modules.add(0, root); SortedDependencies<JSModule> sorter = new SortedDependencies<JSModule>(modules); modules = sorter.getDependenciesOf(modules, true); this.modules = modules; this.moduleGraph = new JSModuleGraph(modules); } catch (Exception e) { Throwables.propagate(e); } } } public Node parse(SourceFile file) { initCompilerOptionsIfTesting(); addToDebugLog("Parsing: " + file.getName()); return new JsAst(file).getAstRoot(this); } private int syntheticCodeId = 0; @Override Node parseSyntheticCode(String js) { CompilerInput input = new CompilerInput( SourceFile.fromCode(" [synthetic:" + (++syntheticCodeId) + "] ", js)); putCompilerInput(input.getInputId(), input); return input.getAstRoot(this); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> /** * Allow subclasses to override the default CompileOptions object. */ protected CompilerOptions newCompilerOptions() { return new CompilerOptions(); } void initCompilerOptionsIfTesting() { if (options == null) { // initialization for tests that don't initialize the compiler // by the normal mechanisms. initOptions(newCompilerOptions()); } } @Override Node parseSyntheticCode(String fileName, String js) { initCompilerOptionsIfTesting(); return parse(SourceFile.fromCode(fileName, js)); } @Override Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( SourceFile.fromCode("[testcode]", js)); if (inputsById == null) { inputsById = Maps.newHashMap(); } putCompilerInput(input.getInputId(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse tree back to JS code. */ public String toSource() { return runInCompilerThread(new Callable<String>() { @Override public String call() throws Exception { Tracer tracer = newTracer("toSource"); try { CodeBuilder cb = new CodeBuilder(); if (jsRoot != null) { int i = 0; for (Node scriptNode = jsRoot.getFirstChild(); scriptNode != null; scriptNode = scriptNode.getNext()) { toSource(cb, i++, scriptNode); } } return cb.toString(); } finally { stopTracer(tracer, "toSource"); } } }); } /** * Converts the parse tree for each input back to JS code. */ public String[] toSourceArray() { return runInCompilerThread(new Callable<String[]>() { @Override public String[] call() throws Exception { Tracer tracer = newTracer("toSourceArray"); try { int numInputs = inputs.size(); String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } finally { stopTracer(tracer, "toSourceArray"); } } }); } /** * Converts the parse tree for a module back to JS code. */ public String toSource(final JSModule module) { return runInCompilerThread(new Callable<String>() { @Override public String call() throws Exception { List<CompilerInput> inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return ""; } CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> throw new IllegalArgumentException( "Bad module: " + module.getName()); } toSource(cb, i, scriptNode); } return cb.toString(); } }); } /** * Converts the parse tree for each input in a module back to JS code. */ public String[] toSourceArray(final JSModule module) { return runInCompilerThread(new Callable<String[]>() { @Override public String[] call() throws Exception { List<CompilerInput> inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return new String[0]; } String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module input: " + inputs.get(i).getName()); } cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } }); } /** * Writes out JS code from a root node. If printing input delimiters, this * method will attach a comment to the start of the text indicating which * input the output derived from. If there were any preserve annotations * within the root's source, they will also be printed in a block comment * at the beginning of the output. */ public void toSource(final CodeBuilder cb, final int inputSeqNum, final Node root) { runInCompilerThread(new Callable<Void>() { @Override public Void call() throws Exception { if (options.printInputDelimiter) { if ((cb.getLength() > 0) && !cb.endsWith("\n")) { cb.append("\n"); // Make sure that the label starts on a new line } Preconditions.checkState(root.isScript()); String delimiter = options.inputDelimiter; String inputName = root.getInputId().getIdName(); String sourceName = root.getSourceFileName(); Preconditions.checkState(sourceName != null); Preconditions.checkState(!sourceName.isEmpty()); delimiter = delimiter .replaceAll("%name%", Matcher.quoteReplacement(inputName)) .replaceAll("%num%", String.valueOf(inputSeqNum)); cb.append(delimiter) .append("\n"); } if (root.getJSDocInfo() != null && root.getJSDocInfo().getLicense() != null) { cb.append("/*\n") .append(root.getJSDocInfo().getLicense()) .append("*/\n"); } // If there is a valid source map, then indicate to it that the current // root node's mappings are offset by the given string builder buffer. if (options.sourceMapOutputPath != null) { sourceMap.setStartingPosition( cb.getLineIndex(), cb.getColumnIndex()); } // if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict' // for the first input file String code = toSource(root, sourceMap, inputSeq

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Num == 0); if (!code.isEmpty()) { cb.append(code); // In order to avoid parse ambiguity when files are concatenated // together, all files should end in a semi-colon. Do a quick // heuristic check if there's an obvious semi-colon already there. int length = code.length(); char lastChar = code.charAt(length - 1); char secondLastChar = length >= 2 ? code.charAt(length - 2) : '\0'; boolean hasSemiColon = lastChar == ';' || (lastChar == '\n' && secondLastChar == ';'); if (!hasSemiColon) { cb.append(";"); } } return null; } }); } /** * Generates JavaScript source code for an AST, doesn't generate source * map info. */ @Override String toSource(Node n) { initCompilerOptionsIfTesting(); return toSource(n, null, true); } /** * Generates JavaScript source code for an AST. */ private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) { CodePrinter.Builder builder = new CodePrinter.Builder(n); builder.setCompilerOptions(options); builder.setSourceMap(sourceMap); builder.setTagAsStrict(firstOutput && options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT); return builder.build(); } /** * Stores a buffer of text to which more can be appended. This is just like a * StringBuilder except that we also track the number of lines. */ public static class CodeBuilder { private final StringBuilder sb = new StringBuilder(); private int lineCount = 0; private int colCount = 0; /** Removes all text, but leaves the line count unchanged. */ void reset() { sb.setLength(0); } /** Appends the given string to the text buffer. */ CodeBuilder append(String str) { sb.append(str); // Adjust the line and column information for the new text. int index = -1; int lastIndex = index; while ((index = str.indexOf('\n', index + 1)) >= 0) { ++lineCount; lastIndex = index; } if (lastIndex == -1) { // No new lines, append the new characters added. colCount += str.length(); } else { colCount = str.length() - (lastIndex + 1); } return this; } /** Returns all text in the text buffer. */ @Override public String toString() { return sb.toString(); } /** Returns the length of the text buffer. */ public int getLength() { return sb.length(); } /** Returns the (zero-based) index of the last line in the text buffer. */ int getLineIndex() { return lineCount; } /** Returns the (zero-based) index of the last column in the text buffer. */ int getColumnIndex() { return colCount; } /** Determines whether the text ends with the given suffix. */ boolean endsWith(String suffix) { return (sb.length()

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> > suffix.length()) && suffix.equals(sb.substring(sb.length() - suffix.length())); } } //------------------------------------------------------------------------ // Optimizations //------------------------------------------------------------------------ public void optimize() { // Ideally, this pass should be the first pass run, however: // 1) VariableReferenceCheck reports unexpected warnings if Normalize // is done first. // 2) ReplaceMessages, stripCode, and potentially custom passes rely on // unmodified local names. normalize(); // Create extern exports after the normalize because externExports depends on unique names. if (options.isExternExportsEnabled() || options.externExportsPath != null) { externExports(); } phaseOptimizer = new PhaseOptimizer(this, tracker, null); if (options.devMode == DevMode.EVERY_PASS) { phaseOptimizer.setSanityCheck(sanityCheck); } if (options.getCheckDeterminism()) { phaseOptimizer.setPrintAstHashcodes(true); } phaseOptimizer.consume(getPassConfig().getOptimizations()); phaseOptimizer.process(externsRoot, jsRoot); phaseOptimizer = null; } @Override void setCssRenamingMap(CssRenamingMap map) { options.cssRenamingMap = map; } @Override CssRenamingMap getCssRenamingMap() { return options.cssRenamingMap; } /** * Reprocesses the current defines over the AST. This is used by GwtCompiler * to generate N outputs for different targets from the same (checked) AST. * For each target, we apply the target-specific defines by calling * {@code processDefines} and then {@code optimize} to optimize the AST * specifically for that target. */ public void processDefines() { (new DefaultPassConfig(options)).processDefines.create(this) .process(externsRoot, jsRoot); } boolean isInliningForbidden() { return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || options.propertyRenaming == PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; } /** Control Flow Analysis. */ ControlFlowGraph<Node> computeCFG() { logger.fine("Computing Control Flow Graph"); Tracer tracer = newTracer("computeCFG"); ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false); process(cfa); stopTracer(tracer, "computeCFG"); return cfa.getCfg(); } public void normalize() { logger.fine("Normalizing"); startPass("normalize"); process(new Normalize(this, false)); endPass(); } @Override void prepareAst(Node root) { CompilerPass pass = new PrepareAst(this); pass.process(null, root); } void recordFunctionInformation() { logger.fine("Recording function information"); startPass("recordFunctionInformation"); RecordFunctionInformation recordFunctionInfoPass = new RecordFunctionInformation( this, getPassConfig().getIntermediateState().functionNames); process(recordFunctionInfoPass); functionInformationMap = recordFunctionInfoPass.getMap(); endPass(); } protected final RecentChange recentChange = new RecentChange(); private final List<CodeChangeHandler

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>> codeChangeHandlers = Lists.<CodeChangeHandler>newArrayList(); /** Name of the synthetic input that holds synthesized externs. */ static final String SYNTHETIC_EXTERNS = "{SyntheticVarsDeclar}"; private CompilerInput synthesizedExternsInput = null; @Override void addChangeHandler(CodeChangeHandler handler) { codeChangeHandlers.add(handler); } @Override void removeChangeHandler(CodeChangeHandler handler) { codeChangeHandlers.remove(handler); } @Override void setScope(Node n) { if (phaseOptimizer != null) { phaseOptimizer.setScope(n); } } @Override Node getJsRoot() { return jsRoot; } @Override boolean hasScopeChanged(Node n) { if (!analyzeChangedScopesOnly || phaseOptimizer == null) { return true; } return phaseOptimizer.hasScopeChanged(n); } @Override void reportChangeToEnclosingScope(Node n) { if (phaseOptimizer != null) { phaseOptimizer.reportChangeToEnclosingScope(n); phaseOptimizer.startCrossScopeReporting(); reportCodeChange(); phaseOptimizer.endCrossScopeReporting(); } else { reportCodeChange(); } } /** * Some tests don't want to call the compiler "wholesale," they may not want * to call check and/or optimize. With this method, tests can execute custom * optimization loops. */ @VisibleForTesting void setPhaseOptimizer(PhaseOptimizer po) { this.phaseOptimizer = po; } @Override public void reportCodeChange() { for (CodeChangeHandler handler : codeChangeHandlers) { handler.reportChange(); } } @Override public CodingConvention getCodingConvention() { CodingConvention convention = options.getCodingConvention(); convention = convention != null ? convention : defaultCodingConvention; return convention; } @Override public boolean isIdeMode() { return options.ideMode; } @Override public boolean acceptEcmaScript5() { switch (options.getLanguageIn()) { case ECMASCRIPT5: case ECMASCRIPT5_STRICT: return true; case ECMASCRIPT3: return false; } throw new IllegalStateException("unexpected language mode"); } public LanguageMode languageMode() { return options.getLanguageIn(); } @Override public boolean acceptConstKeyword() { return options.acceptConstKeyword; } @Override Config getParserConfig() { if (parserConfig == null) { Config.LanguageMode mode; switch (options.getLanguageIn()) { case ECMASCRIPT3: mode = Config.LanguageMode.ECMASCRIPT3; break; case ECMASCRIPT5: mode = Config.LanguageMode.ECMASCRIPT5; break; case ECMASCRIPT5_STRICT: mode = Config.LanguageMode.ECMASCRIPT5_STRICT; break; default: throw new IllegalStateException("unexpected language mode"); } parserConfig = ParserRunner.createConfig( isIdeMode(), mode, acceptConstKeyword(), options.extraAnnotationNames); } return parserConfig

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>; } @Override public boolean isTypeCheckingEnabled() { return options.checkTypes; } //------------------------------------------------------------------------ // Error reporting //------------------------------------------------------------------------ /** * The warning classes that are available from the command-line, and * are suppressible by the {@code @suppress} annotation. */ protected DiagnosticGroups getDiagnosticGroups() { return new DiagnosticGroups(); } @Override public void report(JSError error) { CheckLevel level = error.getDefaultLevel(); if (warningsGuard != null) { CheckLevel newLevel = warningsGuard.level(error); if (newLevel != null) { level = newLevel; } } if (level.isOn()) { if (getOptions().errorHandler != null) { getOptions().errorHandler.report(level, error); } errorManager.report(level, error); } } @Override public CheckLevel getErrorLevel(JSError error) { Preconditions.checkNotNull(options); return warningsGuard.level(error); } /** * Report an internal error. */ @Override void throwInternalError(String message, Exception cause) { String finalMessage = "INTERNAL COMPILER ERROR.\n" + "Please report this problem.\n" + message; RuntimeException e = new RuntimeException(finalMessage, cause); if (cause != null) { e.setStackTrace(cause.getStackTrace()); } throw e; } /** * Gets the number of errors. */ public int getErrorCount() { return errorManager.getErrorCount(); } /** * Gets the number of warnings. */ public int getWarningCount() { return errorManager.getWarningCount(); } @Override boolean hasHaltingErrors() { return !isIdeMode() && getErrorCount() > 0; } /** * Consults the {@link ErrorManager} to see if we've encountered errors * that should halt compilation. <p> * * If {@link CompilerOptions#ideMode} is {@code true}, this function * always returns {@code false} without consulting the error manager. The * error manager will continue to be told about new errors and warnings, but * the compiler will complete compilation of all inputs.<p> */ public boolean hasErrors() { return hasHaltingErrors(); } /** Called from the compiler passes, adds debug info */ @Override void addToDebugLog(String str) { debugLog.append(str); debugLog.append('\n'); logger.fine(str); } @Override SourceFile getSourceFileByName(String sourceName) { // Here we assume that the source name is the input name, this // is try of JavaScript parsed from source. if (sourceName != null) { CompilerInput input = inputsById.get(new InputId(sourceName)); if (input != null) { return input.getSourceFile(); } } return null; } @Override public String getSourceLine(String sourceName, int lineNumber) { if (lineNumber < 1) { return null; } SourceFile input = getSourceFileByName(sourceName); if (input != null) { return input.getLine

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>(lineNumber); } return null; } @Override public Region getSourceRegion(String sourceName, int lineNumber) { if (lineNumber < 1) { return null; } SourceFile input = getSourceFileByName(sourceName); if (input != null) { return input.getRegion(lineNumber); } return null; } //------------------------------------------------------------------------ // Package-private helpers //------------------------------------------------------------------------ @Override Node getNodeForCodeInsertion(JSModule module) { if (module == null) { if (inputs.isEmpty()) { throw new IllegalStateException("No inputs"); } return inputs.get(0).getAstRoot(this); } List<CompilerInput> moduleInputs = module.getInputs(); if (moduleInputs.size() > 0) { return moduleInputs.get(0).getAstRoot(this); } throw new IllegalStateException("Root module has no inputs"); } public SourceMap getSourceMap() { return sourceMap; } VariableMap getVariableMap() { return getPassConfig().getIntermediateState().variableMap; } VariableMap getPropertyMap() { return getPassConfig().getIntermediateState().propertyMap; } CompilerOptions getOptions() { return options; } FunctionInformationMap getFunctionalInformationMap() { return functionInformationMap; } /** * Sets the logging level for the com.google.javascript.jscomp package. */ public static void setLoggingLevel(Level level) { logger.setLevel(level); } /** Gets the DOT graph of the AST generated at the end of compilation. */ public String getAstDotGraph() throws IOException { if (jsRoot != null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false); cfa.process(null, jsRoot); return DotFormatter.toDot(jsRoot, cfa.getCfg()); } else { return ""; } } @Override public ErrorManager getErrorManager() { if (options == null) { initOptions(newCompilerOptions()); } return errorManager; } @Override List<CompilerInput> getInputsInOrder() { return Collections.<CompilerInput>unmodifiableList(inputs); } /** * Returns an unmodifiable view of the compiler inputs indexed by id. */ public Map<InputId, CompilerInput> getInputsById() { return Collections.unmodifiableMap(inputsById); } /** * Gets the externs in the order in which they are being processed. */ List<CompilerInput> getExternsInOrder() { return Collections.<CompilerInput>unmodifiableList(externs); } /** * Stores the internal compiler state just before optimization is performed. * This can be saved and restored in order to efficiently optimize multiple * different output targets without having to perform checking multiple times. * * NOTE: This does not include all parts of the compiler's internal state. In * particular, SourceFiles and CompilerOptions are not recorded. In * order to recreate a Compiler instance from scratch, you would need to * call {@code init} with the same arguments as in the initial creation before * restoring intermediate state. */ public static class IntermediateState implements Serializable

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> { private static final long serialVersionUID = 1L; Node externsRoot; private Node jsRoot; private List<CompilerInput> externs; private List<CompilerInput> inputs; private List<JSModule> modules; private PassConfig.State passConfigState; private JSTypeRegistry typeRegistry; private AbstractCompiler.LifeCycleStage lifeCycleStage; private Map<String, Node> injectedLibraries; private IntermediateState() {} } /** * Returns the current internal state, excluding the input files and modules. */ public IntermediateState getState() { IntermediateState state = new IntermediateState(); state.externsRoot = externsRoot; state.jsRoot = jsRoot; state.externs = externs; state.inputs = inputs; state.modules = modules; state.passConfigState = getPassConfig().getIntermediateState(); state.typeRegistry = typeRegistry; state.lifeCycleStage = getLifeCycleStage(); state.injectedLibraries = Maps.newLinkedHashMap(injectedLibraries); return state; } /** * Sets the internal state to the capture given. Note that this assumes that * the input files are already set up. */ public void setState(IntermediateState state) { externsRoot = state.externsRoot; jsRoot = state.jsRoot; externs = state.externs; inputs = state.inputs; modules = state.modules; passes = createPassConfigInternal(); getPassConfig().setIntermediateState(state.passConfigState); typeRegistry = state.typeRegistry; setLifeCycleStage(state.lifeCycleStage); injectedLibraries.clear(); injectedLibraries.putAll(state.injectedLibraries); } @VisibleForTesting List<CompilerInput> getInputsForTesting() { return inputs; } @VisibleForTesting List<CompilerInput> getExternsForTesting() { return externs; } @Override boolean hasRegExpGlobalReferences() { return hasRegExpGlobalReferences; } @Override void setHasRegExpGlobalReferences(boolean references) { hasRegExpGlobalReferences = references; } @Override void updateGlobalVarReferences(Map<Var, ReferenceCollection> refMapPatch, Node collectionRoot) { Preconditions.checkState(collectionRoot.isScript() || collectionRoot.isBlock()); if (globalRefMap == null) { globalRefMap = new GlobalVarReferenceMap(getInputsInOrder(), getExternsInOrder()); } globalRefMap.updateGlobalVarReferences(refMapPatch, collectionRoot); } @Override GlobalVarReferenceMap getGlobalVarReferences() { return globalRefMap; } @Override CompilerInput getSynthesizedExternsInput() { if (synthesizedExternsInput == null) { synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS); } return synthesizedExternsInput; } @Override public double getProgress() { return progress; } @Override String getLastPassName() { return lastPassName; } @Override void setProgress(double newProgress, String passName) { this.lastPassName = passName

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>; if (newProgress > 1.0) { progress = 1.0; } else { progress = newProgress; } } /** * Replaces one file in a hot-swap mode. The given JsAst should be made * from a new version of a file that already was present in the last compile * call. If the file is new, this will silently ignored. * * @param ast the ast of the file that is being replaced */ public void replaceScript(JsAst ast) { CompilerInput input = this.getInput(ast.getInputId()); if (!replaceIncrementalSourceAst(ast)) { return; } Node originalRoot = input.getAstRoot(this); processNewScript(ast, originalRoot); } /** * Adds a new Script AST to the compile state. If a script for the same file * already exists the script will not be added, instead a call to * #replaceScript should be used. * * @param ast the ast of the new file */ public void addNewScript(JsAst ast) { if (!addNewSourceAst(ast)) { return; } Node emptyScript = new Node(Token.SCRIPT); InputId inputId = ast.getInputId(); emptyScript.setInputId(inputId); emptyScript.setStaticSourceFile( SourceFile.fromCode(inputId.getIdName(), "")); processNewScript(ast, emptyScript); } private void processNewScript(JsAst ast, Node originalRoot) { Node js = ast.getAstRoot(this); Preconditions.checkNotNull(js); runHotSwap(originalRoot, js, this.getCleanupPassConfig()); // NOTE: If hot swap passes that use GlobalNamespace are added, we will need // to revisit this approach to clearing GlobalNamespaces runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks); this.getTypeRegistry().clearNamedTypes(); this.removeSyntheticVarsInput(); runHotSwap(originalRoot, js, this.ensureDefaultPassConfig()); } /** * Execute the passes from a PassConfig instance over a single replaced file. */ private void runHotSwap( Node originalRoot, Node js, PassConfig passConfig) { for (PassFactory passFactory : passConfig.getChecks()) { runHotSwapPass(originalRoot, js, passFactory); } } private void runHotSwapPass( Node originalRoot, Node js, PassFactory passFactory) { HotSwapCompilerPass pass = passFactory.getHotSwapPass(this); if (pass != null) { logger.info("Performing HotSwap for pass " + passFactory.getName()); pass.hotSwapScript(js, originalRoot); } } private PassConfig getCleanupPassConfig() { return new CleanupPasses(getOptions()); } private void removeSyntheticVarsInput() { String sourceName = Compiler.SYNTHETIC_EXTERNS; removeExternInput(new InputId(sourceName)); } @Override Node ensureLibraryInjected(String resourceName) { if (injectedLibraries.containsKey(resourceName)) { return null; } // All libraries depend on js/base.js boolean

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> isBase = "base".equals(resourceName); if (!isBase) { ensureLibraryInjected("base"); } Node firstChild = loadLibraryCode(resourceName).removeChildren(); Node lastChild = firstChild.getLastSibling(); Node parent = getNodeForCodeInsertion(null); if (isBase) { parent.addChildrenToFront(firstChild); } else { parent.addChildrenAfter( firstChild, injectedLibraries.get("base")); } reportCodeChange(); injectedLibraries.put(resourceName, lastChild); return lastChild; } /** Load a library as a resource */ @VisibleForTesting Node loadLibraryCode(String resourceName) { String originalCode; try { originalCode = CharStreams.toString(new InputStreamReader( Compiler.class.getResourceAsStream( String.format("js/%s.js", resourceName)), Charsets.UTF_8)); } catch (IOException e) { throw new RuntimeException(e); } return Normalize.parseAndNormalizeSyntheticCode( this, originalCode, String.format("jscomp_%s_", resourceName)); } /** Returns the compiler version baked into the jar. */ public static String getReleaseVersion() { ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); return config.getString("compiler.version"); } /** Returns the compiler date baked into the jar. */ public static String getReleaseDate() { ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); return config.getString("compiler.date"); } /** * {@inheritDoc} */ @Override public void setOldParseTree(String sourceName, AstRoot oldAst) { } /** * {@inheritDoc} */ @Override public AstRoot getOldParseTreeByName(String sourceName) { return null; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.parsing.ParserRunner; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import java.io.IOException; import java.util.logging.Logger; /** * Generates an AST for a JavaScript source file. * */ public class JsAst implements SourceAst { private static final Logger logger_ = Logger.getLogger(JsAst.class.getName()); private static final long serialVersionUID = 1L; private transient InputId inputId; private transient SourceFile sourceFile; private String fileName; private Node root; public JsAst(SourceFile sourceFile) { this.inputId = new InputId(sourceFile.getName()); this.sourceFile = sourceFile; this.fileName = sourceFile.getName(); } @Override public Node getAstRoot(AbstractCompiler compiler) { if (root == null) { parse(compiler); root.setInputId(inputId); } return root; } @Override public void clearAst() { root = null; // While we're at it, clear out any saved text in the source file on // the assumption that if we're dumping the parse tree, then we probably // assume regenerating everything else is a smart idea also. sourceFile.clearCachedSource(); } @Override public InputId getInputId() { return inputId; } @Override public SourceFile getSourceFile() { return sourceFile; } @Override public void setSourceFile(SourceFile file) { Preconditions.checkState(fileName.equals(file.getName())); sourceFile = file; } private void parse(AbstractCompiler compiler) { int startErrorCount = compiler.getErrorManager().getErrorCount(); try { ParserRunner.ParseResult result = ParserRunner.parse(sourceFile, sourceFile.getCode(), compiler.getParserConfig(), compiler.getDefaultErrorReporter(), logger_); root = result.ast; compiler.setOldParseTree(sourceFile.getName(), result.oldAst); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); } if (root == null || // Most passes try to report as many errors as possible, // so there may already be errors. We only care if there were // errors in the code we just

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; /** * A source excerpt provider is responsible for building source code excerpt * of specific locations, such as a specific line or a region around a * given line number. * */ public interface SourceExcerptProvider { /** * Source excerpt variety. */ enum SourceExcerpt { /** * Line excerpt. */ LINE { @Override public String get(SourceExcerptProvider source, String sourceName, int lineNumber, ExcerptFormatter formatter) { return formatter.formatLine( source.getSourceLine(sourceName, lineNumber), lineNumber); } }, /** * Region excerpt. */ REGION { @Override public String get(SourceExcerptProvider source, String sourceName, int lineNumber, ExcerptFormatter formatter) { return formatter.formatRegion( source.getSourceRegion(sourceName, lineNumber)); } }; /** * Get a source excerpt string based on the type of the source excerpt. */ public abstract String get(SourceExcerptProvider source, String sourceName, int lineNumber, ExcerptFormatter formatter); } /** * Get the line indicated by the line number. This call will return only the * specific line. * * @param lineNumber the line number, 1 being the first line of the file * @return the line indicated, or {@code null} if it does not exist */ String getSourceLine(String sourceName, int lineNumber); /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file * @return the region around the line number indicated, or <code>null</null> * if it does not exist */ Region getSourceRegion(String sourceName, int lineNumber); /** * A excerpt formatter is responsible of formatting source excerpts. */ interface ExcerptFormatter { /** * Format a line excerpt. */ String formatLine(String line, int lineNumber); /** * Format a region excerpt. */ String formatRegion(Region region); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> assigns other types of values to this property, * the property will take on the union of all these types. * * UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN * type has all properties, but we do not know whether they are * declared or inferred. * */ public abstract class ObjectType extends JSType implements StaticScope<JSType> { private boolean visited; private JSDocInfo docInfo = null; private boolean unknown = true; ObjectType(JSTypeRegistry registry) { super(registry); } ObjectType(JSTypeRegistry registry, TemplateTypeMap templateTypeMap) { super(registry, templateTypeMap); } @Override public Node getRootNode() { return null; } @Override public ObjectType getParentScope() { return getImplicitPrototype(); } /** * Returns the property map that manages the set of properties for an object. */ PropertyMap getPropertyMap() { return PropertyMap.immutableEmptyMap(); } /** * Default getSlot implementation. This gets overridden by FunctionType * for lazily-resolved prototypes. */ @Override public Property getSlot(String name) { return getPropertyMap().getSlot(name); } @Override public Property getOwnSlot(String name) { return getPropertyMap().getOwnProperty(name); } @Override public JSType getTypeOfThis() { return null; } /** * Gets the declared default element type. * @see TemplatizedType */ public ImmutableList<JSType> getTemplateTypes() { return null; } /** * Gets the docInfo for this type. */ @Override public JSDocInfo getJSDocInfo() { return docInfo; } /** * Sets the docInfo for this type from the given * {@link JSDocInfo}. The {@code JSDocInfo} may be {@code null}. */ public void setJSDocInfo(JSDocInfo info) { docInfo = info; } /** * Detects a cycle in the implicit prototype chain. This method accesses * the {@link #getImplicitPrototype()} method and must therefore be * invoked only after the object is sufficiently initialized to respond to * calls to this method.<p> * * @return True iff an implicit prototype cycle was detected. */ final boolean detectImplicitPrototypeCycle() { // detecting cycle this.visited = true; ObjectType p = getImplicitPrototype(); while (p != null) { if (p.visited) { return true; } else { p.visited = true; } p = p.getImplicitPrototype(); } // clean up p = this; do { p.visited = false; p = p.getImplicitPrototype(); } while (p != null); return false; } /** * Detects cycles in either the implicit prototype chain, or the implemented/extended * interfaces.<p> * * @return True iff a cycle was detected. */ final boolean detectInheritanceCycle() { // TODO(user): This should get moved to preventing cycles in FunctionTypeBuilder // rather than removing them here after they have been created. // Also, this doesn't do the right thing for extended interfaces

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>, though that is // masked by another bug. return detectImplicitPrototypeCycle() || Iterables.contains(this.getCtorImplementedInterfaces(), this) || Iterables.contains(this.getCtorExtendedInterfaces(), this); } /** * Gets the reference name for this object. This includes named types * like constructors, prototypes, and enums. It notably does not include * literal types like strings and booleans and structural types. * @return the object's name or {@code null} if this is an anonymous * object */ public abstract String getReferenceName(); /** * Due to the complexity of some of our internal type systems, sometimes * we have different types constructed by the same constructor. * In other parts of the type system, these are called delegates. * We construct these types by appending suffixes to the constructor name. * * The normalized reference name does not have these suffixes, and as such, * recollapses these implicit types back to their real type. */ public String getNormalizedReferenceName() { String name = getReferenceName(); if (name != null) { int pos = name.indexOf("("); if (pos != -1) { return name.substring(0, pos); } } return name; } @Override public String getDisplayName() { return getNormalizedReferenceName(); } /** * Creates a suffix for a proxy delegate. * @see #getNormalizedReferenceName */ public static String createDelegateSuffix(String suffix) { return "(" + suffix + ")"; } /** * Returns true if the object is named. * @return true if the object is named, false if it is anonymous */ public boolean hasReferenceName() { return false; } @Override public TernaryValue testForEquality(JSType that) { // super TernaryValue result = super.testForEquality(that); if (result != null) { return result; } // objects are comparable to everything but null/undefined if (that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } else { return FALSE; } } /** * Gets this object's constructor. * @return this object's constructor or {@code null} if it is a native * object (constructed natively v.s. by instantiation of a function) */ public abstract FunctionType getConstructor(); /** * Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property). */ public abstract ObjectType getImplicitPrototype(); /** * Defines a property whose type is explicitly declared by the programmer. * @param propertyName the property's name * @param type the type * @param propertyNode the node corresponding to the declaration of property * which might later be accessed using {@code getPropertyNode}. */ public final boolean defineDeclaredProperty(String propertyName, JSType type, Node propertyNode) { boolean result = defineProperty(propertyName, type, false, propertyNode); // All property definitions go through this method // or defineInferredProperty. Because the properties defined an an // object can affect subtyping, it's slightly more efficient // to

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>param propertyName the name of the property * @return the {@code Node} corresponding to the property or null. */ public Node getPropertyNode(String propertyName) { Property p = getSlot(propertyName); return p == null ? null : p.getNode(); } /** * Gets the docInfo on the specified property on this type. This should not * be implemented recursively, as you generally need to know exactly on * which type in the prototype chain the JSDocInfo exists. */ public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { Property p = getOwnSlot(propertyName); return p == null ? null : p.getJSDocInfo(); } /** * Sets the docInfo for the specified property from the * {@link JSDocInfo} on its definition. * @param info {@code JSDocInfo} for the property definition. May be * {@code null}. */ public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { // by default, do nothing } @Override public JSType findPropertyType(String propertyName) { return hasProperty(propertyName) ? getPropertyType(propertyName) : null; } /** * Gets the property type of the property whose name is given. If the * underlying object does not have this property, the Unknown type is * returned to indicate that no information is available on this property. * * This gets overridden by FunctionType for lazily-resolved call() and * bind() functions. * * @return the property's type or {@link UnknownType}. This method never * returns {@code null}. */ public JSType getPropertyType(String propertyName) { StaticSlot<JSType> slot = getSlot(propertyName); if (slot == null) { if (isNoResolvedType() || isCheckedUnknownType()) { return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); } else if (isEmptyType()) { return getNativeType(JSTypeNative.NO_TYPE); } return getNativeType(JSTypeNative.UNKNOWN_TYPE); } return slot.getType(); } @Override public boolean hasProperty(String propertyName) { // Unknown types have all properties. return isEmptyType() || isUnknownType() || getSlot(propertyName) != null; } /** * Checks whether the property whose name is given is present directly on * the object. Returns false even if it is declared on a supertype. */ public boolean hasOwnProperty(String propertyName) { return getOwnSlot(propertyName) != null; } /** * Returns the names of all the properties directly on this type. * * Overridden by FunctionType to add "prototype". */ public Set<String> getOwnPropertyNames() { return getPropertyMap().getOwnPropertyNames(); } /** * Checks whether the property's type is inferred. */ public boolean isPropertyTypeInferred(String propertyName) { StaticSlot<JSType> slot = getSlot(propertyName); return slot == null ? false : slot.isTypeInferred(); } /** * Checks whether the property's type is declared. */ public boolean isPropertyTypeDeclared(String propertyName) { StaticSlot<JS

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Type> slot = getSlot(propertyName); return slot == null ? false : !slot.isTypeInferred(); } /** * Whether the given property is declared on this object. */ final boolean hasOwnDeclaredProperty(String name) { return hasOwnProperty(name) && isPropertyTypeDeclared(name); } /** Checks whether the property was defined in the externs. */ public boolean isPropertyInExterns(String propertyName) { Property p = getSlot(propertyName); return p == null ? false : p.isFromExterns(); } /** * Gets the number of properties of this object. */ public int getPropertiesCount() { return getPropertyMap().getPropertiesCount(); } /** * Returns a list of properties defined or inferred on this type and any of * its supertypes. */ public Set<String> getPropertyNames() { Set<String> props = Sets.newTreeSet(); collectPropertyNames(props); return props; } /** * Adds any properties defined on this type or its supertypes to the set. */ final void collectPropertyNames(Set<String> props) { getPropertyMap().collectPropertyNames(props); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseObjectType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseObjectType(this, that); } /** * Checks that the prototype is an implicit prototype of this object. Since * each object has an implicit prototype, an implicit prototype's * implicit prototype is also this implicit prototype's. * * @param prototype any prototype based object * * @return {@code true} if {@code prototype} is {@code equal} to any * object in this object's implicit prototype chain. */ final boolean isImplicitPrototype(ObjectType prototype) { for (ObjectType current = this; current != null; current = current.getImplicitPrototype()) { if (current.isTemplatizedType()) { current = current.toMaybeTemplatizedType().getReferencedType(); } if (current.isEquivalentTo(prototype)) { return true; } } return false; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.TRUE; } /** * We treat this as the unknown type if any of its implicit prototype * properties is unknown. */ @Override public boolean isUnknownType() { // If the object is unknown now, check the supertype again, // because it might have been resolved since the last check. if (unknown) { ObjectType implicitProto = getImplicitPrototype(); if (implicitProto == null || implicitProto.isNativeObjectType()) { unknown = false; for (ObjectType interfaceType : getCtorExtendedInterfaces()) { if (interfaceType.isUnknownType()) { unknown = true; break; } } } else { unknown = implicitProto.isUnknownType(); } } return unknown; } @Override public boolean isObject() { return true; } /** * Returns true if any cached values have been set for

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> this type. If true, * then the prototype chain should not be changed, as it might invalidate the * cached values. */ public boolean hasCachedValues() { return !unknown; } /** * Clear cached values. Should be called before making changes to a prototype * that may have been changed since creation. */ public void clearCachedValues() { unknown = true; } /** Whether this is a built-in object. */ public boolean isNativeObjectType() { return false; } /** * A null-safe version of JSType#toObjectType. */ public static ObjectType cast(JSType type) { return type == null ? null : type.toObjectType(); } @Override public final boolean isFunctionPrototypeType() { return getOwnerFunction() != null; } /** Gets the owner of this if it's a function prototype. */ public FunctionType getOwnerFunction() { return null; } /** Sets the owner function. By default, does nothing. */ void setOwnerFunction(FunctionType type) {} /** * Gets the interfaces implemented by the ctor associated with this type. * Intended to be overridden by subclasses. */ public Iterable<ObjectType> getCtorImplementedInterfaces() { return ImmutableSet.of(); } /** * Gets the interfaces extended by the interface associated with this type. * Intended to be overridden by subclasses. */ public Iterable<ObjectType> getCtorExtendedInterfaces() { return ImmutableSet.of(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.io.Serializable; import java.util.Arrays; import java.util.Map; import java.util.Set; /** * Group a set of related diagnostic types together, so that they can * be toggled on and off as one unit. * @author nicksantos@google.com (Nick Santos) */ public class DiagnosticGroup implements Serializable { private static final long serialVersionUID = 1; // The set of types represented by this group, hashed by key. private final Set<DiagnosticType> types; // A human-readable name for the group. private final String name; /** * Create a group that matches all errors of the given types. */ DiagnosticGroup(String name, DiagnosticType ...types) { this.name = name; this.types = ImmutableSet.copyOf(Arrays.asList(types)); } /** * Create a group that matches all errors of the given types. */ public DiagnosticGroup(DiagnosticType ...types) { this(null, types); } /** * Create a diagnostic group with no name that only matches the given type. */ private DiagnosticGroup(DiagnosticType type) { this.name = null; this.types = ImmutableSet.of(type); } // DiagnosticGroups with only a single DiagnosticType. private static final Map<DiagnosticType, DiagnosticGroup> singletons = Maps.newHashMap(); /** Create a diagnostic group that matches only the given type. */ public static DiagnosticGroup forType(DiagnosticType type) { if (!singletons.containsKey(type)) { singletons.put(type, new DiagnosticGroup(type)); } return singletons.get(type); } /** * Create a composite group. */ public DiagnosticGroup(DiagnosticGroup ...groups) { this(null, groups); } /** * Create a composite group. */ public DiagnosticGroup(String name, DiagnosticGroup ...groups) { Set<DiagnosticType> set = Sets.newHashSet(); for (DiagnosticGroup group : groups) { set.addAll(group.types); } this.name = name; this.types = ImmutableSet.copyOf(set); } /** * Returns whether the given error's type matches a type * in this group. */ public boolean matches(JSError error) { return matches(error.getType

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>()); } /** * Returns whether the given type matches a type in this group. */ public boolean matches(DiagnosticType type) { return types.contains(type); } /** * Returns whether all of the types in the given group are in this group. */ boolean isSubGroup(DiagnosticGroup group) { for (DiagnosticType type : group.types) { if (!matches(type)) { return false; } } return true; } /** * Returns an iterable over all the types in this group. */ public Iterable<DiagnosticType> getTypes() { return types; } @Override public String toString() { return name == null ? super.toString() : "DiagnosticGroup<" + name + ">"; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import java.util.logging.Level; import java.util.logging.Logger; /** * An error manager that logs errors and warnings using a logger in addition to * collecting them in memory. Errors are logged at the SEVERE level and warnings * are logged at the WARNING level. * */ public class LoggerErrorManager extends BasicErrorManager { private final MessageFormatter formatter; private final Logger logger; /** * Creates an instance. */ public LoggerErrorManager(MessageFormatter formatter, Logger logger) { this.formatter = formatter; this.logger = logger; } /** * Creates an instance with a source-less error formatter. */ public LoggerErrorManager(Logger logger) { this(ErrorFormat.SOURCELESS.toFormatter(null, false), logger); } @Override public void println(CheckLevel level, JSError error) { switch (level) { case ERROR: logger.severe(error.format(level, formatter)); break; case WARNING: logger.warning(error.format(level, formatter)); break; case OFF: break; } } @Override protected void printSummary() { Level level = (getErrorCount() + getWarningCount() == 0) ? Level.INFO : Level.WARNING; if (getTypedPercent() > 0.0) { logger.log(level, "{0} error(s), {1} warning(s), {2,number,#.#}% typed", new Object[] {getErrorCount(), getWarningCount(), getTypedPercent()}); } else { if (getErrorCount() + getWarningCount() > 0) { logger.log(level, "{0} error(s), {1} warning(s)", new Object[] {getErrorCount(), getWarningCount()}); } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> {@link FunctionType} or * {@link EnumType}.<p> * * If full typedefs are to be supported, then each method on each type class * needs to be reviewed to make sure that everything works correctly through * typedefs. Alternatively, we would need to walk through the parse tree and * unroll each reference to a {@code NamedType} to its resolved type before * applying the rest of the analysis.<p> * * TODO(user): Revisit all of this logic.<p> * * The existing typing logic is hacky. Unresolved types should get processed * in a more consistent way, but with the Rhino merge coming, there will be * much that has to be changed.<p> * */ class NamedType extends ProxyObjectType { private static final long serialVersionUID = 1L; private final String reference; private final String sourceName; private final int lineno; private final int charno; /** * Validates the type resolution. */ private Predicate<JSType> validator; /** * Property-defining continuations. */ private List<PropertyContinuation> propertyContinuations = null; /** * Create a named type based on the reference. */ NamedType(JSTypeRegistry registry, String reference, String sourceName, int lineno, int charno) { super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); Preconditions.checkNotNull(reference); this.reference = reference; this.sourceName = sourceName; this.lineno = lineno; this.charno = charno; } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { if (!isResolved()) { // If this is an unresolved object type, we need to save all its // properties and define them when it is resolved. if (propertyContinuations == null) { propertyContinuations = Lists.newArrayList(); } propertyContinuations.add( new PropertyContinuation( propertyName, type, inferred, propertyNode)); return true; } else { return super.defineProperty( propertyName, type, inferred, propertyNode); } } private void finishPropertyContinuations() { ObjectType referencedObjType = getReferencedObjTypeInternal(); if (referencedObjType != null && !referencedObjType.isUnknownType()) { if (propertyContinuations != null) { for (PropertyContinuation c : propertyContinuations) { c.commit(this); } } } propertyContinuations = null; } /** Returns the type to which this refers (which is unknown if unresolved). */ public JSType getReferencedType() { return getReferencedTypeInternal(); } @Override public String getReferenceName() { return reference; } @Override String toStringHelper(boolean forAnnotations) { return reference; } @Override public boolean hasReferenceName() { return true; } @Override boolean isNamedType() { return true; } @Override public boolean isNominalType() { return true; } @Override public int hashCode() { return reference.hashCode(); } /** * Resolve the referenced type within the enclosing scope. */ @Override

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>length() == 0) { return null; } StaticSlot<JSType> slot = enclosing.getSlot(componentNames[0]); if (slot == null) { return null; } // If the first component has a type of 'Unknown', then any type // names using it should be regarded as silently 'Unknown' rather than be // noisy about it. JSType slotType = slot.getType(); if (slotType == null || slotType.isAllType() || slotType.isNoType()) { return null; } JSType value = getTypedefType(reporter, slot); if (value == null) { return null; } // resolving component by component for (int i = 1; i < componentNames.length; i++) { ObjectType parentClass = ObjectType.cast(value); if (parentClass == null) { return null; } if (componentNames[i].length() == 0) { return null; } value = parentClass.getPropertyType(componentNames[i]); } return value; } private void setReferencedAndResolvedType( JSType type, ErrorReporter reporter) { if (validator != null) { validator.apply(type); } setReferencedType(type); checkEnumElementCycle(reporter); checkProtoCycle(reporter); setResolvedTypeInternal(getReferencedType()); } private void handleTypeCycle(ErrorReporter t) { setReferencedType( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); warning(t, "Cycle detected in inheritance chain of type " + reference); setResolvedTypeInternal(getReferencedType()); } private void checkEnumElementCycle(ErrorReporter t) { JSType referencedType = getReferencedType(); if (referencedType instanceof EnumElementType && ((EnumElementType) referencedType).getPrimitiveType() == this) { handleTypeCycle(t); } } private void checkProtoCycle(ErrorReporter t) { JSType referencedType = getReferencedType(); if (referencedType == this) { handleTypeCycle(t); } } // Warns about this type being unresolved iff it's not a forward-declared // type name. private void handleUnresolvedType( ErrorReporter t, boolean ignoreForwardReferencedTypes) { if (registry.isLastGeneration()) { boolean isForwardDeclared = ignoreForwardReferencedTypes && registry.isForwardDeclaredType(reference); if (!isForwardDeclared && registry.isLastGeneration()) { warning(t, "Bad type annotation. Unknown type " + reference); } else { setReferencedType( registry.getNativeObjectType( JSTypeNative.NO_RESOLVED_TYPE)); if (registry.isLastGeneration() && validator != null) { validator.apply(getReferencedType()); } } setResolvedTypeInternal(getReferencedType()); } else { setResolvedTypeInternal(this); } } private JSType getTypedefType(ErrorReporter t, StaticSlot<JSType> slot) { JSType type = slot.getType(); if (type != null) { return type; } handleUnresolvedType(t, true); return null; }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> @Override public boolean setValidator(Predicate<JSType> validator) { // If the type is already resolved, we can validate it now. If // the type has not been resolved yet, we need to wait till its // resolved before we can validate it. if (this.isResolved()) { return super.setValidator(validator); } else { this.validator = validator; return true; } } void warning(ErrorReporter reporter, String message) { reporter.warning(message, sourceName, lineno, charno); } /** Store enough information to define a property at a later time. */ private static final class PropertyContinuation { private final String propertyName; private final JSType type; private final boolean inferred; private final Node propertyNode; private PropertyContinuation( String propertyName, JSType type, boolean inferred, Node propertyNode) { this.propertyName = propertyName; this.type = type; this.inferred = inferred; this.propertyNode = propertyNode; } void commit(ObjectType target) { target.defineProperty( propertyName, type, inferred, propertyNode); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * <p>The syntactic scope creator scans the parse tree to create a Scope object * containing all the variable declarations in that scope.</p> * * <p>This implementation is not thread-safe.</p> * */ class SyntacticScopeCreator implements ScopeCreator { private final AbstractCompiler compiler; private Scope scope; private InputId inputId; private final RedeclarationHandler redeclarationHandler; // The arguments variable is special, in that it's declared in every local // scope, but not explicitly declared. private static final String ARGUMENTS = "arguments"; /** * Creates a ScopeCreator. */ SyntacticScopeCreator(AbstractCompiler compiler) { this.compiler = compiler; this.redeclarationHandler = new DefaultRedeclarationHandler(); } SyntacticScopeCreator( AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } @Override public Scope createScope(Node n, Scope parent) { inputId = null; if (parent == null) { scope = Scope.createGlobalScope(n); } else { scope = new Scope(parent, n); } scanRoot(n); inputId = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n) { if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnNameNode); } // Args: Declare function variables Preconditions.checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.getNext()) { Preconditions.checkState(a.isName()); declareVar(a); } // Body scanVars(body); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); declareVar(child); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) {} } /** * Declares a variable. * * @param n The node corresponding to the variable name. */ private void declareVar(Node n) { Preconditions.checkState(n.isName()); CompilerInput input = compiler.getInput(inputId); String name = n.getString(); if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, input); } else { scope.declare(name, n, null, input); } } /** * Generates an untyped global scope from the root of AST of compiler (which * includes externs). * * @param compiler The

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> { DiGraphNode<N, Branch> node = getCfg().getImplicitReturn(); FlowState<L> state = node.getAnnotation(); return state.getIn(); } @SuppressWarnings("unchecked") protected L join(L latticeA, L latticeB) { return joinOp.apply(Lists.<L>newArrayList(latticeA, latticeB)); } /** * Checks whether the analysis is a forward flow analysis or backward flow * analysis. * * @return {@code true} if it is a forward analysis. */ abstract boolean isForward(); /** * Computes the output state for a given node and input state. * * @param node The node. * @param input Input lattice that should be read-only. * @return Output lattice. */ abstract L flowThrough(N node, L input); /** * Finds a fixed-point solution using at most {@link #MAX_STEPS} * iterations. * * @see #analyze(int) */ final void analyze() { analyze(MAX_STEPS); } /** * Finds a fixed-point solution. The function has the side effect of replacing * the existing node annotations with the computed solutions using {@link * com.google.javascript.jscomp.graph.GraphNode#setAnnotation(Annotation)}. * * <p>Initially, each node's input and output flow state contains the value * given by {@link #createInitialEstimateLattice()} (with the exception of the * entry node of the graph which takes on the {@link #createEntryLattice()} * value. Each node will use the output state of its predecessor and compute a * output state according to the instruction. At that time, any nodes that * depends on the node's newly modified output value will need to recompute * their output state again. Each step will perform a computation at one node * until no extra computation will modify any existing output state anymore. * * @param maxSteps Max number of iterations before the method stops and throw * a {@link MaxIterationsExceededException}. This will prevent the * analysis from going into a infinite loop. */ final void analyze(int maxSteps) { initialize(); int step = 0; while (!orderedWorkSet.isEmpty()) { if (step > maxSteps) { throw new MaxIterationsExceededException( "Analysis did not terminate after " + maxSteps + " iterations"); } DiGraphNode<N, Branch> curNode = orderedWorkSet.iterator().next(); orderedWorkSet.remove(curNode); joinInputs(curNode); if (flow(curNode)) { // If there is a change in the current node, we want to grab the list // of nodes that this node affects. List<DiGraphNode<N, Branch>> nextNodes = isForward() ? cfg.getDirectedSuccNodes(curNode) : cfg.getDirectedPredNodes(curNode); for (DiGraphNode<N, Branch> nextNode : nextNodes) { if (nextNode != cfg.getImplicitReturn()) { orderedWorkSet.add(nextNode); } } } step++; } if (isForward()) { joinInputs(getCfg().getImplicitReturn());

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } } /** * Gets the state of the initial estimation at each node. * * @return Initial state. */ abstract L createInitialEstimateLattice(); /** * Gets the incoming state of the entry node. * * @return Entry state. */ abstract L createEntryLattice(); /** * Initializes the work list and the control flow graph. */ protected void initialize() { // TODO(user): Calling clear doesn't deallocate the memory in a // LinkedHashSet. Consider creating a new work set if we plan to repeatedly // call analyze. orderedWorkSet.clear(); for (DiGraphNode<N, Branch> node : cfg.getDirectedGraphNodes()) { node.setAnnotation(new FlowState<L>(createInitialEstimateLattice(), createInitialEstimateLattice())); if (node != cfg.getImplicitReturn()) { orderedWorkSet.add(node); } } } /** * Performs a single flow through a node. * * @return {@code true} if the flow state differs from the previous state. */ protected boolean flow(DiGraphNode<N, Branch> node) { FlowState<L> state = node.getAnnotation(); if (isForward()) { L outBefore = state.out; state.out = flowThrough(node.getValue(), state.in); return !outBefore.equals(state.out); } else { L inBefore = state.in; state.in = flowThrough(node.getValue(), state.out); return !inBefore.equals(state.in); } } /** * Computes the new flow state at a given node's entry by merging the * output (input) lattice of the node's predecessor (successor). * * @param node Node to compute new join. */ protected void joinInputs(DiGraphNode<N, Branch> node) { FlowState<L> state = node.getAnnotation(); if (isForward()) { if (cfg.getEntry() == node) { state.setIn(createEntryLattice()); } else { List<DiGraphNode<N, Branch>> inNodes = cfg.getDirectedPredNodes(node); if (inNodes.size() == 1) { FlowState<L> inNodeState = inNodes.get(0).getAnnotation(); state.setIn(inNodeState.getOut()); } else if (inNodes.size() > 1) { List<L> values = new ArrayList<L>(inNodes.size()); for (DiGraphNode<N, Branch> currentNode : inNodes) { FlowState<L> currentNodeState = currentNode.getAnnotation(); values.add(currentNodeState.getOut()); } state.setIn(joinOp.apply(values)); } } } else { List<DiGraphNode<N, Branch>> inNodes = cfg.getDirectedSuccNodes(node); if (inNodes.size() == 1) { DiGraphNode<N, Branch> inNode = inNodes.get(0); if (inNode == cfg.getImplicitReturn()) { state.setOut(createEntryLattice()); } else { FlowState<

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>L> inNodeState = inNode.getAnnotation(); state.setOut(inNodeState.getIn()); } } else if (inNodes.size() > 1) { List<L> values = new ArrayList<L>(inNodes.size()); for (DiGraphNode<N, Branch> currentNode : inNodes) { FlowState<L> currentNodeState = currentNode.getAnnotation(); values.add(currentNodeState.getIn()); } state.setOut(joinOp.apply(values)); } } } /** * The in and out states of a node. * * @param <L> Input and output lattice element type. */ static class FlowState<L extends LatticeElement> implements Annotation { private L in; private L out; /** * Private constructor. No other classes should create new states. * * @param inState Input. * @param outState Output. */ private FlowState(L inState, L outState) { Preconditions.checkNotNull(inState); Preconditions.checkNotNull(outState); this.in = inState; this.out = outState; } L getIn() { return in; } void setIn(L in) { Preconditions.checkNotNull(in); this.in = in; } L getOut() { return out; } void setOut(L out) { Preconditions.checkNotNull(out); this.out = out; } @Override public String toString() { return String.format("IN: %s OUT: %s", in, out); } @Override public int hashCode() { return Objects.hashCode(in, out); } } /** * The exception to be thrown if the analysis has been running for a long * number of iterations. Chances are the analysis is not monotonic, a * fixed-point cannot be found and it is currently stuck in an infinite loop. */ static class MaxIterationsExceededException extends RuntimeException { private static final long serialVersionUID = 1L; MaxIterationsExceededException(String msg) { super(msg); } } abstract static class BranchedForwardDataFlowAnalysis <N, L extends LatticeElement> extends DataFlowAnalysis<N, L> { @Override protected void initialize() { orderedWorkSet.clear(); for (DiGraphNode<N, Branch> node : getCfg().getDirectedGraphNodes()) { int outEdgeCount = getCfg().getOutEdges(node.getValue()).size(); List<L> outLattices = Lists.newArrayList(); for (int i = 0; i < outEdgeCount; i++) { outLattices.add(createInitialEstimateLattice()); } node.setAnnotation(new BranchedFlowState<L>( createInitialEstimateLattice(), outLattices)); if (node != getCfg().getImplicitReturn()) { orderedWorkSet.add(node); } } } BranchedForwardDataFlowAnalysis(ControlFlowGraph<N> targetCfg, JoinOp<L> joinOp) { super(targetCfg, joinOp); } /** * Returns the lattice element at the exit point. Needs to be overridden

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * because we use a BranchedFlowState instead of a FlowState; ugh. */ @Override L getExitLatticeElement() { DiGraphNode<N, Branch> node = getCfg().getImplicitReturn(); BranchedFlowState<L> state = node.getAnnotation(); return state.getIn(); } @Override final boolean isForward() { return true; } /** * The branched flow function maps a single lattice to a list of output * lattices. * * <p>Each outgoing edge of a node will have a corresponding output lattice * in the ordered returned by * {@link com.google.javascript.jscomp.graph.DiGraph#getOutEdges(Object)} * in the returned list. * * @return A list of output values depending on the edge's branch type. */ abstract List<L> branchedFlowThrough(N node, L input); @Override protected final boolean flow(DiGraphNode<N, Branch> node) { BranchedFlowState<L> state = node.getAnnotation(); List<L> outBefore = state.out; state.out = branchedFlowThrough(node.getValue(), state.in); Preconditions.checkState(outBefore.size() == state.out.size()); for (int i = 0; i < outBefore.size(); i++) { if (!outBefore.get(i).equals(state.out.get(i))) { return true; } } return false; } @Override protected void joinInputs(DiGraphNode<N, Branch> node) { BranchedFlowState<L> state = node.getAnnotation(); List<DiGraphNode<N, Branch>> predNodes = getCfg().getDirectedPredNodes(node); List<L> values = new ArrayList<L>(predNodes.size()); for (DiGraphNode<N, Branch> predNode : predNodes) { BranchedFlowState<L> predNodeState = predNode.getAnnotation(); L in = predNodeState.out.get( getCfg().getDirectedSuccNodes(predNode).indexOf(node)); values.add(in); } if (getCfg().getEntry() == node) { state.setIn(createEntryLattice()); } else if (!values.isEmpty()) { state.setIn(joinOp.apply(values)); } } } /** * The in and out states of a node. * * @param <L> Input and output lattice element type. */ static class BranchedFlowState<L extends LatticeElement> implements Annotation { private L in; private List<L> out; /** * Private constructor. No other classes should create new states. * * @param inState Input. * @param outState Output. */ private BranchedFlowState(L inState, List<L> outState) { Preconditions.checkNotNull(inState); Preconditions.checkNotNull(outState); this.in = inState; this.out = outState; } L getIn() { return in; } void setIn(L in) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Preconditions.checkNotNull(in); this.in = in; } List<L> getOut() { return out; } void setOut(List<L> out) { Preconditions.checkNotNull(out); for (L item : out) { Preconditions.checkNotNull(item); } this.out = out; } @Override public String toString() { return String.format("IN: %s OUT: %s", in, out); } @Override public int hashCode() { return Objects.hashCode(in, out); } } /** * Compute set of escaped variables. When a variable is escaped in a * dataflow analysis, it can be reference outside of the code that we are * analyzing. A variable is escaped if any of the following is true: * * <p><ol> * <li>It is defined as the exception name in CATCH clause so it became a * variable local not to our definition of scope.</li> * <li>Exported variables as they can be needed after the script terminates. * </li> * <li>Names of named functions because in JavaScript, <i>function foo(){}</i> * does not kill <i>foo</i> in the dataflow.</li> */ static void computeEscaped(final Scope jsScope, final Set<Var> escaped, AbstractCompiler compiler) { // TODO(user): Very good place to store this information somewhere. AbstractPostOrderCallback finder = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (jsScope == t.getScope() || !n.isName() || parent.isFunction()) { return; } String name = n.getString(); Var var = t.getScope().getVar(name); if (var != null && var.scope == jsScope) { escaped.add(jsScope.getVar(name)); } } }; NodeTraversal t = new NodeTraversal(compiler, finder); t.traverseAtScope(jsScope); // 1: Remove the exception name in CATCH which technically isn't local to // begin with. for (Iterator<Var> i = jsScope.getVars(); i.hasNext();) { Var var = i.next(); if (var.getParentNode().isCatch() || compiler.getCodingConvention().isExported(var.getName())) { escaped.add(var); } } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2011 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; /** * {@link CheckDebuggerStatement} checks for the presence of the "debugger" * statement in JavaScript code. It is appropriate to use this statement while * developing JavaScript; however, it is generally undesirable to include it in * production code. * * @author bolinfest@google.com (Michael Bolin) */ class CheckDebuggerStatement extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType DEBUGGER_STATEMENT_PRESENT = DiagnosticType.disabled("JSC_DEBUGGER_STATEMENT_PRESENT", "Using the debugger statement can halt your application if the user " + "has a JavaScript debugger running."); private final AbstractCompiler compiler; public CheckDebuggerStatement(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isDebugger()) { t.report(n, DEBUGGER_STATEMENT_PRESENT); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt; /** * Error formats available. */ public enum ErrorFormat { LEGACY { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { VerboseMessageFormatter formatter = new VerboseMessageFormatter(source); formatter.setColorize(colorize); return formatter; } }, SINGLELINE { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { LightweightMessageFormatter formatter = new LightweightMessageFormatter( source); formatter.setColorize(colorize); return formatter; } }, MULTILINE { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { LightweightMessageFormatter formatter = new LightweightMessageFormatter( source, SourceExcerpt.REGION); formatter.setColorize(colorize); return formatter; } }, SOURCELESS { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { LightweightMessageFormatter formatter = LightweightMessageFormatter.withoutSource(); formatter.setColorize(colorize); return formatter; } }; /** * Convert to a concrete formatter. */ public abstract MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> final DiagnosticType BAD_FUNCTION_DECLARATION = DiagnosticType.error( "JSC_BAD_FUNCTION_DECLARATION", "functions can only be declared at top level or immediately within " + "another function in ES5 strict mode"); private final AbstractCompiler compiler; private final boolean noVarCheck; private final boolean noCajaChecks; StrictModeCheck(AbstractCompiler compiler) { this(compiler, false, false); } StrictModeCheck( AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) { this.compiler = compiler; this.noVarCheck = noVarCheck; this.noCajaChecks = noCajaChecks; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); NodeTraversal.traverse(compiler, root, new NonExternChecks()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { checkFunctionUse(t, n); } else if (n.isName()) { if (!isDeclaration(n)) { checkNameUse(t, n); } } else if (n.isAssign()) { checkAssignment(t, n); } else if (n.isDelProp()) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteral(t, n); } else if (n.isLabel()) { checkLabel(t, n); } } /** Checks that the function is used legally. */ private void checkFunctionUse(NodeTraversal t, Node n) { if (NodeUtil.isFunctionDeclaration(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { t.report(n, BAD_FUNCTION_DECLARATION); } } /** * Determines if the given name is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getType()) { case Token.VAR: case Token.FUNCTION: case Token.CATCH: return true; case Token.PARAM_LIST: return n.getParent().getParent().isFunction(); default: return false; } } /** Checks that the given name is used legally. */ private void checkNameUse(NodeTraversal t, Node n) { Var v = t.getScope().getVar(n.getString()); if (v == null) { // In particular, this prevents creating a global variable by assigning // to it without a declaration. if (!noVarCheck) { t.report(n, UNKNOWN_VARIABLE, n.getString()); } } if (!noCajaChecks) { if ("eval".equals(n.getString())) { t.report(n, EVAL_USE); } else if (n.getString().endsWith("__")) { t.report(n, ILLEGAL_NAME); } } } /** Checks that an assignment is not to the "arguments" object. */ private void checkAssignment(NodeTraversal t, Node n) { if (n

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.getFirstChild().isName()) { if ("arguments".equals(n.getFirstChild().getString())) { t.report(n, ARGUMENTS_ASSIGNMENT); } else if ("eval".equals(n.getFirstChild().getString())) { // Note that assignment to eval is already illegal because any use of // that name is illegal. if (noCajaChecks) { t.report(n, EVAL_ASSIGNMENT); } } } } /** Checks that variables, functions, and arguments are not deleted. */ private void checkDelete(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { Var v = t.getScope().getVar(n.getFirstChild().getString()); if (v != null) { t.report(n, DELETE_VARIABLE); } } } /** Checks that object literal keys are valid. */ private void checkObjectLiteral(NodeTraversal t, Node n) { Set<String> getters = Sets.newHashSet(); Set<String> setters = Sets.newHashSet(); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (!noCajaChecks && key.getString().endsWith("__")) { t.report(key, ILLEGAL_NAME); } if (!key.isSetterDef()) { // normal property and getter cases if (getters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { getters.add(key.getString()); } } if (!key.isGetterDef()) { // normal property and setter cases if (setters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { setters.add(key.getString()); } } } } /** Checks that label names are valid. */ private void checkLabel(NodeTraversal t, Node n) { if (n.getFirstChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getFirstChild(), ILLEGAL_NAME); } } } /** Checks that are performed on non-extern code only. */ private class NonExternChecks extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if ((n.isName()) && isDeclaration(n)) { checkDeclaration(t, n); } else if (n.isGetProp()) { checkProperty(t, n); } } /** Checks for illegal declarations. */ private void checkDeclaration(NodeTraversal t, Node n) { if ("eval".equals(n.getString())) { t.report(n, EVAL_DECLARATION); } else if ("arguments".equals(n.getString())) { t.report(n, ARGUMENTS_DECLARATION); } else if (n.getString().endsWith("__")) { if (!noCajaChecks) { t.report(n, ILLEGAL_NAME); } } } /** Checks for illegal property accesses. */ private void checkProperty(NodeTraversal t, Node n) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * Bottom type, representing the subclass of any value or object. * * Although JavaScript programmers can't explicitly denote the bottom type, * it comes up in static analysis. For example, if we have: * <code> * var x = null; * if (x) { * f(x); * } * </code> * We need to be able to assign {@code x} a type within the {@code f(x)} * call. Since it has no possible type, we assign {@code x} the NoType, * so that {@code f(x)} is legal no matter what the type of {@code f}'s * first argument is. * * @see <a href="http://en.wikipedia.org/wiki/Bottom_type">Bottom types</a> */ public class NoType extends NoObjectType { private static final long serialVersionUID = 1L; NoType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNoObjectType() { return false; } @Override public boolean isNoType() { return true; } @Override public boolean isNullable() { return true; } @Override public boolean isSubtype(JSType that) { return true; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> return BooleanLiteralSet.EMPTY; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNoType(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseNoType(that); } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "None"; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> new EnumElementType(registry, elementsType, name); } /** * Gets the source node or null if this is an unknown enum. */ public Node getSource() { return source; } @Override public EnumType toMaybeEnumType() { return this; } @Override public ObjectType getImplicitPrototype() { return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); } /** * Gets the elements defined on this enum. * @return the elements' names defined on this enum. The returned set is * immutable. */ public Set<String> getElements() { return Collections.unmodifiableSet(elements); } /** * Defines a new element on this enum. * @param name the name of the new element * @param definingNode the {@code Node} that defines this new element * @return true iff the new element is added successfully */ public boolean defineElement(String name, Node definingNode) { elements.add(name); return defineDeclaredProperty(name, elementsType, definingNode); } /** * Gets the elements' type. */ public EnumElementType getElementsType() { return elementsType; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } return this.isEquivalentTo(that) ? TRUE : FALSE; } @Override public boolean isSubtype(JSType that) { return that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_TYPE)) || that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_PROTOTYPE)) || JSType.isSubtypeHelper(this, that); } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "Object" : getReferenceName(); } @Override public String getDisplayName() { return elementsType.getDisplayName(); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseObjectType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseObjectType(this, that); } @Override public FunctionType getConstructor() { return null; } @Override public boolean matchesNumberContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { elementsType = (EnumElementType) elementsType.resolve(t, scope); return super.resolveInternal(t, scope); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>INITIALIZED_USING_NEW_SYNTAX = DiagnosticType.error("JSC_MSG_NOT_INITIALIZED_USING_NEW_SYNTAX", "message not initialized using " + MSG_FUNCTION_NAME); static final DiagnosticType BAD_FALLBACK_SYNTAX = DiagnosticType.error("JSC_MSG_BAD_FALLBACK_SYNTAX", String.format( "Bad syntax. " + "Expected syntax: goog.getMsgWithFallback(MSG_1, MSG_2)", MSG_FALLBACK_FUNCTION_NAME)); static final DiagnosticType FALLBACK_ARG_ERROR = DiagnosticType.error("JSC_MSG_FALLBACK_ARG_ERROR", "Could not find message entry for fallback argument {0}"); private static final String PH_JS_PREFIX = "{$"; private static final String PH_JS_SUFFIX = "}"; static final String MSG_PREFIX = "MSG_"; /** * Pattern for unnamed messages. * <p> * All JS messages in JS code should have unique name but messages in * generated code (i.e. from soy template) could have duplicated message names. * Later we replace the message names with ids constructed as a hash of the * message content. * <p> * <link href="http://code.google.com/p/closure-templates/"> * Soy</link> generates messages with names MSG_UNNAMED_<NUMBER> . This * pattern recognizes such messages. */ private static final Pattern MSG_UNNAMED_PATTERN = Pattern.compile("MSG_UNNAMED_\\d+"); private static final Pattern CAMELCASE_PATTERN = Pattern.compile("[a-z][a-zA-Z\\d]*[_\\d]*"); static final String HIDDEN_DESC_PREFIX = "@hidden"; // For old-style JS messages private static final String DESC_SUFFIX = "_HELP"; private final boolean needToCheckDuplications; private final JsMessage.Style style; private final JsMessage.IdGenerator idGenerator; final AbstractCompiler compiler; /** * The names encountered associated with their defining node and source. We * use it for tracking duplicated message ids in the source code. */ private final Map<String, MessageLocation> messageNames = Maps.newHashMap(); private final Map<Var, JsMessage> unnamedMessages = Maps.newHashMap(); /** * List of found goog.getMsg call nodes. * * When we visit goog.getMsg() node we add a pair node:sourcename and later * when we visit its parent we remove this pair. All nodes that are left at * the end of traversing are orphaned nodes. It means have no corresponding * var or property node. */ private final Map<Node, String> googMsgNodes = Maps.newHashMap(); private final CheckLevel checkLevel; /** * Creates JS message visitor. * * @param compiler the compiler instance * @param needToCheckDuplications whether to check duplicated messages in * traversed * @param style style that should be used during parsing * @param idGenerator generator that used for creating unique ID for the * message */ JsMessageVisitor(AbstractCompiler compiler, boolean needToCheckDuplic

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>ations, JsMessage.Style style, JsMessage.IdGenerator idGenerator) { this.compiler = compiler; this.needToCheckDuplications = needToCheckDuplications; this.style = style; this.idGenerator = idGenerator; checkLevel = (style == JsMessage.Style.CLOSURE) ? CheckLevel.ERROR : CheckLevel.WARNING; // TODO(anatol): add flag that decides whether to process UNNAMED messages. // Some projects would not want such functionality (unnamed) as they don't // use SOY templates. } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); for (Map.Entry<Node, String> msgNode : googMsgNodes.entrySet()) { compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(), checkLevel, MESSAGE_NODE_IS_ORPHANED)); } } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { String messageKey; boolean isVar; Node msgNode, msgNodeParent; switch (node.getType()) { case Token.NAME: // var MSG_HELLO = 'Message' if ((parent != null) && (parent.isVar())) { messageKey = node.getString(); isVar = true; } else { return; } msgNode = node.getFirstChild(); msgNodeParent = node; break; case Token.ASSIGN: // somenamespace.someclass.MSG_HELLO = 'Message' isVar = false; Node getProp = node.getFirstChild(); if (!getProp.isGetProp()) { return; } Node propNode = getProp.getLastChild(); messageKey = propNode.getString(); msgNode = node.getLastChild(); msgNodeParent = node; break; case Token.CALL: // goog.getMsg() String fnName = node.getFirstChild().getQualifiedName(); if (MSG_FUNCTION_NAME.equals(fnName)) { googMsgNodes.put(node, traversal.getSourceName()); } else if (MSG_FALLBACK_FUNCTION_NAME.equals(fnName)) { visitFallbackFunctionCall(traversal, node); } return; default: return; } // Is this a message name? boolean isNewStyleMessage = msgNode != null && msgNode.isCall(); if (!isMessageName(messageKey, isNewStyleMessage)) { return; } if (msgNode == null) { compiler.report( traversal.makeError(node, MESSAGE_HAS_NO_VALUE, messageKey)); return; } // Just report a warning if a qualified messageKey that looks like a message // (e.g. "a.b.MSG_X") doesn't use goog.getMsg(). if (isNewStyleMessage) { googMsgNodes.remove(msgNode); } else if (style != JsMessage.Style.LEGACY) { compiler.report(traversal.makeError(node, checkLevel, MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX)); } boolean isUnnamedMsg = is

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * @return String representation of the node * @throws MalformedException if the parsed message is invalid */ private static String extractStringFromStringExprNode(Node node) throws MalformedException { switch (node.getType()) { case Token.STRING: return node.getString(); case Token.ADD: StringBuilder sb = new StringBuilder(); for (Node child : node.children()) { sb.append(extractStringFromStringExprNode(child)); } return sb.toString(); default: throw new MalformedException("STRING or ADD node expected; found: " + getReadableTokenName(node), node); } } /** * Initializes a message builder from a FUNCTION node. * <p> * <pre> * The tree should look something like: * * function * |-- name * |-- lp * | |-- name <arg1> * | -- name <arg2> * -- block * | * --return * | * --add * |-- string foo * -- name <arg1> * </pre> * * @param builder the message builder * @param node the function node that contains a message * @throws MalformedException if the parsed message is invalid */ private void extractFromFunctionNode(Builder builder, Node node) throws MalformedException { Set<String> phNames = Sets.newHashSet(); for (Node fnChild : node.children()) { switch (fnChild.getType()) { case Token.NAME: // This is okay. The function has a name, but it is empty. break; case Token.PARAM_LIST: // Parse the placeholder names from the function argument list. for (Node argumentNode : fnChild.children()) { if (argumentNode.isName()) { String phName = argumentNode.getString(); if (phNames.contains(phName)) { throw new MalformedException("Duplicate placeholder name: " + phName, argumentNode); } else { phNames.add(phName); } } } break; case Token.BLOCK: // Build the message's value by examining the return statement Node returnNode = fnChild.getFirstChild(); if (!returnNode.isReturn()) { throw new MalformedException("RETURN node expected; found: " + getReadableTokenName(returnNode), returnNode); } for (Node child : returnNode.children()) { extractFromReturnDescendant(builder, child); } // Check that all placeholders from the message text have appropriate // object literal keys for (String phName : builder.getPlaceholders()) { if (!phNames.contains(phName)) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phName, returnNode); } } break; default: throw new MalformedException( "NAME, LP, or BLOCK node expected; found: " + getReadableTokenName(node), fnChild); } } } /** * Appends value parts to the message builder by traversing the descendants * of the given RETURN node. * * @param builder the message builder * @

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> phName, aNode); } if (phNames.contains(phName)) { throw new MalformedException("Duplicate placeholder name: " + phName, aNode); } phNames.add(phName); } } // Check that all placeholders from the message text have appropriate objlit // values Set<String> usedPlaceholders = builder.getPlaceholders(); for (String phName : usedPlaceholders) { if (!phNames.contains(phName)) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phName, objLitNode); } } // Check that objLiteral have only names that are present in the // message text for (String phName : phNames) { if (!usedPlaceholders.contains(phName)) { throw new MalformedException( "Unused message placeholder: " + phName, objLitNode); } } } /** * Appends the message parts in a JS message value extracted from the given * text node. * * @param builder the JS message builder to append parts to * @param node the node with string literal that contains the message text * @throws MalformedException if {@code value} contains a reference to * an unregistered placeholder */ private void parseMessageTextNode(Builder builder, Node node) throws MalformedException { String value = extractStringFromStringExprNode(node); while (true) { int phBegin = value.indexOf(PH_JS_PREFIX); if (phBegin < 0) { // Just a string literal builder.appendStringPart(value); return; } else { if (phBegin > 0) { // A string literal followed by a placeholder builder.appendStringPart(value.substring(0, phBegin)); } // A placeholder. Find where it ends int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin); if (phEnd < 0) { throw new MalformedException( "Placeholder incorrectly formatted in: " + builder.getKey(), node); } String phName = value.substring(phBegin + PH_JS_PREFIX.length(), phEnd); builder.appendPlaceholderReference(phName); int nextPos = phEnd + PH_JS_SUFFIX.length(); if (nextPos < value.length()) { // Iterate on the rest of the message value value = value.substring(nextPos); } else { // The message is parsed return; } } } } /** Visit a call to goog.getMsgWithFallback. */ private void visitFallbackFunctionCall(NodeTraversal t, Node call) { // Check to make sure the function call looks like: // goog.getMsgWithFallback(MSG_1, MSG_2); if (call.getChildCount() != 3 || !call.getChildAtIndex(1).isName() || !call.getChildAtIndex(2).isName()) { compiler.report(t.makeError(call, BAD_FALLBACK_SYNTAX)); return; } Node firstArg = call.getChildAtIndex(1); JsMessage firstMessage = getTrackedMessage(t, firstArg.getString()); if (

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>pre> * Foo.a; * Bar.a; * </pre> * * <p> will become * * <pre> * Foo.a$Foo; * Bar.a$Bar; * </pre> * */ class DisambiguateProperties<T> implements CompilerPass { // To prevent the logs from filling up, we cap the number of warnings // that we tell the user to fix per-property. private static final int MAX_INVALDIATION_WARNINGS_PER_PROPERTY = 10; private static final Logger logger = Logger.getLogger( DisambiguateProperties.class.getName()); static class Warnings { // TODO(user): {1} and {2} are not exactly useful for most people. static final DiagnosticType INVALIDATION = DiagnosticType.disabled( "JSC_INVALIDATION", "Property disambiguator skipping all instances of property {0} " + "because of type {1} node {2}. {3}"); static final DiagnosticType INVALIDATION_ON_TYPE = DiagnosticType.disabled( "JSC_INVALIDATION_TYPE", "Property disambiguator skipping instances of property {0} " + "on type {1}. {2}"); } private final AbstractCompiler compiler; private final TypeSystem<T> typeSystem; /** * Map of a type to all the related errors that invalidated the type * for disambiguation. It has be Object because of the generic nature of * this pass. */ private Multimap<Object, JSError> invalidationMap; /** * In practice any large code base will have thousands and thousands of * type invalidations, which makes reporting all of the errors useless. * However, certain properties are worth specifically guarding because of the * large amount of code that can be removed as dead code. This list contains * the properties (eg: "toString") that we care about; if any of these * properties is invalidated it causes an error. */ private final Map<String, CheckLevel> propertiesToErrorFor; private class Property { /** The name of the property. */ final String name; /** All types on which the field exists, grouped together if related. */ private UnionFind<T> types; /** * A set of types for which renaming this field should be skipped. This * list is first filled by fields defined in the externs file. */ Set<T> typesToSkip = Sets.newHashSet(); /** * If true, do not rename any instance of this field, as it has been * referenced from an unknown type. */ boolean skipRenaming; /** Set of nodes for this field that need renaming. */ Set<Node> renameNodes = Sets.newHashSet(); /** * Map from node to the highest type in the prototype chain containing the * field for that node. In the case of a union, the type is the highest type * of one of the types in the union. */ final Map<Node, T> rootTypes = Maps.newHashMap(); Property(String name) { this.name = name; } /** Returns the types on which this field is referenced. */ UnionFind<T> getTypes

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>() { if (types == null) { types = new StandardUnionFind<T>(); } return types; } /** * Record that this property is referenced from this type. * @return true if the type was recorded for this property, else false, * which would happen if the type was invalidating. */ boolean addType(T type, T top, T relatedType) { checkState(!skipRenaming, "Attempt to record skipped property: %s", name); if (typeSystem.isInvalidatingType(top)) { invalidate(); return false; } else { if (typeSystem.isTypeToSkip(top)) { addTypeToSkip(top); } if (relatedType == null) { getTypes().add(top); } else { getTypes().union(top, relatedType); } typeSystem.recordInterfaces(type, top, this); return true; } } /** Records the given type as one to skip for this property. */ void addTypeToSkip(T type) { for (T skipType : typeSystem.getTypesToSkipForType(type)) { typesToSkip.add(skipType); getTypes().union(skipType, type); } } /** Invalidates any types related to invalid types. */ void expandTypesToSkip() { // If we are not going to rename any properties, then we do not need to // update the list of invalid types, as they are all invalid. if (shouldRename()) { int count = 0; while (true) { // It should usually only take one time through this do-while. checkState(++count < 10, "Stuck in loop expanding types to skip."); // Make sure that the representative type for each type to skip is // marked as being skipped. Set<T> rootTypesToSkip = Sets.newHashSet(); for (T subType : typesToSkip) { rootTypesToSkip.add(types.find(subType)); } typesToSkip.addAll(rootTypesToSkip); Set<T> newTypesToSkip = Sets.newHashSet(); Set<T> allTypes = types.elements(); int originalTypesSize = allTypes.size(); for (T subType : allTypes) { if (!typesToSkip.contains(subType) && typesToSkip.contains(types.find(subType))) { newTypesToSkip.add(subType); } } for (T newType : newTypesToSkip) { addTypeToSkip(newType); } // If there were not any new types added, we are done here. if (types.elements().size() == originalTypesSize) { break; } } } } /** Returns true if any instance of this property should be renamed. */ boolean shouldRename() { return !skipRenaming && types != null && types.allEquivalenceClasses().size() > 1; } /** * Returns true if this property should be renamed on this type. * expandTypesToSkip() should be called before this, if anything has been * added to the typesToSkip list. */ boolean shouldRename(T

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> type) { return !skipRenaming && !typesToSkip.contains(type); } /** * Invalidates a field from renaming. Used for field references on an * object with unknown type. */ boolean invalidate() { boolean changed = !skipRenaming; skipRenaming = true; types = null; return changed; } /** * Schedule the node to potentially be renamed. * @param node the node to rename * @param type the highest type in the prototype chain for which the * property is defined * @return True if type was accepted without invalidation or if the property * was already invalidated. False if this property was invalidated this * time. */ boolean scheduleRenaming(Node node, T type) { if (!skipRenaming) { if (typeSystem.isInvalidatingType(type)) { invalidate(); return false; } renameNodes.add(node); rootTypes.put(node, type); } return true; } } private Map<String, Property> properties = Maps.newHashMap(); static DisambiguateProperties<JSType> forJSTypeSystem( AbstractCompiler compiler, Map<String, CheckLevel> propertiesToErrorFor) { return new DisambiguateProperties<JSType>( compiler, new JSTypeSystem(compiler), propertiesToErrorFor); } static DisambiguateProperties<ConcreteType> forConcreteTypeSystem( AbstractCompiler compiler, TightenTypes tt, Map<String, CheckLevel> propertiesToErrorFor) { return new DisambiguateProperties<ConcreteType>( compiler, new ConcreteTypeSystem(tt, compiler.getCodingConvention()), propertiesToErrorFor); } /** * This constructor should only be called by one of the helper functions * above for either the JSType system, or the concrete type system. */ private DisambiguateProperties(AbstractCompiler compiler, TypeSystem<T> typeSystem, Map<String, CheckLevel> propertiesToErrorFor) { this.compiler = compiler; this.typeSystem = typeSystem; this.propertiesToErrorFor = propertiesToErrorFor; if (!this.propertiesToErrorFor.isEmpty()) { this.invalidationMap = LinkedHashMultimap.create(); } else { this.invalidationMap = null; } } @Override public void process(Node externs, Node root) { Preconditions.checkState( compiler.getLifeCycleStage() == LifeCycleStage.NORMALIZED); for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { addInvalidatingType(mis.typeA, mis.src); addInvalidatingType(mis.typeB, mis.src); } NodeTraversal.traverse(compiler, externs, new FindExternProperties()); NodeTraversal.traverse(compiler, root, new FindRenameableProperties()); renameProperties(); } private void recordInvalidationError(JSType t, JSError error) { if (!t.isObject()) { return; } if (invalidationMap != null) { invalidationMap.put(t, error); } } /** * Invalidates the given type, so that no properties on it

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> will be renamed. */ private void addInvalidatingType(JSType type, JSError error) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { addInvalidatingType(alt, error); } } else if (type.isEnumElementType()) { addInvalidatingType( type.toMaybeEnumElementType().getPrimitiveType(), error); } else { typeSystem.addInvalidatingType(type); recordInvalidationError(type, error); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getImplicitPrototype() != null) { typeSystem.addInvalidatingType(objType.getImplicitPrototype()); recordInvalidationError(objType.getImplicitPrototype(), error); } } } /** Returns the property for the given name, creating it if necessary. */ protected Property getProperty(String name) { if (!properties.containsKey(name)) { properties.put(name, new Property(name)); } return properties.get(name); } /** Public for testing. */ T getTypeWithProperty(String field, T type) { return typeSystem.getTypeWithProperty(field, type); } /** Tracks the current type system scope while traversing. */ private abstract class AbstractScopingCallback implements ScopedCallback { protected final Stack<StaticScope<T>> scopes = new Stack<StaticScope<T>>(); @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { scopes.push(typeSystem.getRootScope()); } else { scopes.push(typeSystem.getFunctionScope(t.getScopeRoot())); } } @Override public void exitScope(NodeTraversal t) { scopes.pop(); } /** Returns the current scope at this point in the file. */ protected StaticScope<T> getScope() { return scopes.peek(); } } /** * Finds all properties defined in the externs file and sets them as * ineligible for renaming from the type on which they are defined. */ private class FindExternProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { // TODO(johnlenz): Support object-literal property definitions. if (n.isGetProp()) { String field = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), field); Property prop = getProperty(field); if (typeSystem.isInvalidatingType(type)) { prop.invalidate(); } else { prop.addTypeToSkip(type); // If this is a prototype property, then we want to skip assignments // to the instance type as well. These assignments are not usually // seen in the extern code itself, so we must handle them here. if ((type = typeSystem.getInstanceFromPrototype(type)) != null) { prop.getTypes().add(type); prop.typesTo

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Skip.add(type); } } } } } /** * Traverses the tree, building a map from field names to Nodes for all * fields that can be renamed. */ private class FindRenameableProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { handleGetProp(t, n); } else if (n.isObjectLit()) { handleObjectLit(t, n); } } /** * Processes a GETPROP node. */ private void handleGetProp(NodeTraversal t, Node n) { String name = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), name); Property prop = getProperty(name); if (!prop.scheduleRenaming(n.getLastChild(), processProperty(t, prop, type, null))) { if (propertiesToErrorFor.containsKey(name)) { String suggestion = ""; if (type instanceof JSType) { JSType jsType = (JSType) type; if (jsType.isAllType() || jsType.isUnknownType()) { if (n.getFirstChild().isThis()) { suggestion = "The \"this\" object is unknown in the function," + "consider using @this"; } else { String qName = n.getFirstChild().getQualifiedName(); suggestion = "Consider casting " + qName + " if you know it's type."; } } else { List<String> errors = Lists.newArrayList(); printErrorLocations(errors, jsType); if (!errors.isEmpty()) { suggestion = "Consider fixing errors for the following types:\n"; suggestion += Joiner.on("\n").join(errors); } } } compiler.report(JSError.make( t.getSourceName(), n, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), suggestion)); } } } /** * Processes a OBJECTLIT node. */ private void handleObjectLit(NodeTraversal t, Node n) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // Maybe STRING, GET, SET if (child.isQuotedString()) { continue; } // We should never see a mix of numbers and strings. String name = child.getString(); T type = typeSystem.getType(getScope(), n, name); Property prop = getProperty(name); if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) { // TODO(user): It doesn't look like the user can do much in this // case right now. if (propertiesToErrorFor.containsKey(name)) { compiler.report(JSError.make( t.getSourceName(), child, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), "")); } } } } private void printErrorLocations(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>List<String> errors, JSType t) { if (!t.isObject() || t.isAllType()) { return; } if (t.isUnionType()) { for (JSType alt : t.toMaybeUnionType().getAlternates()) { printErrorLocations(errors, alt); } return; } for (JSError error : invalidationMap.get(t)) { if (errors.size() > MAX_INVALDIATION_WARNINGS_PER_PROPERTY) { return; } errors.add( t.toString() + " at " + error.sourceName + ":" + error.lineNumber); } } /** * Processes a property, adding it to the list of properties to rename. * @return a representative type for the property reference, which will be * the highest type on the prototype chain of the provided type. In the * case of a union type, it will be the highest type on the prototype * chain of one of the members of the union. */ private T processProperty( NodeTraversal t, Property prop, T type, T relatedType) { type = typeSystem.restrictByNotNullOrUndefined(type); if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) { return null; } Iterable<T> alternatives = typeSystem.getTypeAlternatives(type); if (alternatives != null) { T firstType = relatedType; for (T subType : alternatives) { T lastType = processProperty(t, prop, subType, firstType); if (lastType != null) { firstType = firstType == null ? lastType : firstType; } } return firstType; } else { T topType = typeSystem.getTypeWithProperty(prop.name, type); if (typeSystem.isInvalidatingType(topType)) { return null; } prop.addType(type, topType, relatedType); return topType; } } } /** Renames all properties with references on more than one type. */ void renameProperties() { int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0, instancesSkipped = 0, singleTypeProps = 0; Set<String> reported = Sets.newHashSet(); for (Property prop : properties.values()) { if (prop.shouldRename()) { Map<T, String> propNames = buildPropNames(prop.getTypes(), prop.name); ++propsRenamed; prop.expandTypesToSkip(); for (Node node : prop.renameNodes) { T rootType = prop.rootTypes.get(node); if (prop.shouldRename(rootType)) { String newName = propNames.get(rootType); node.setString(newName); compiler.reportCodeChange(); ++instancesRenamed; } else { ++instancesSkipped; CheckLevel checkLevelForProp = propertiesToErrorFor.get(prop.name); if (checkLevelForProp != null && checkLevelForProp != CheckLevel.OFF && !reported.contains(prop.name)) { reported.add(prop.name); compiler.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>report(JSError.make( NodeUtil.getSourceName(node), node, checkLevelForProp, Warnings.INVALIDATION_ON_TYPE, prop.name, rootType.toString(), "")); } } } } else { if (prop.skipRenaming) { ++propsSkipped; } else { ++singleTypeProps; } } } logger.fine("Renamed " + instancesRenamed + " instances of " + propsRenamed + " properties."); logger.fine("Skipped renaming " + instancesSkipped + " invalidated " + "properties, " + propsSkipped + " instances of properties " + "that were skipped for specific types and " + singleTypeProps + " properties that were referenced from only one type."); } /** * Chooses a name to use for renaming in each equivalence class and maps * each type in that class to it. */ private Map<T, String> buildPropNames(UnionFind<T> types, String name) { Map<T, String> names = Maps.newHashMap(); for (Set<T> set : types.allEquivalenceClasses()) { checkState(!set.isEmpty()); String typeName = null; for (T type : set) { if (typeName == null || type.toString().compareTo(typeName) < 0) { typeName = type.toString(); } } String newName; if ("{...}".equals(typeName)) { newName = name; } else { newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name; } for (T type : set) { names.put(type, newName); } } return names; } /** Returns a map from field name to types for which it will be renamed. */ Multimap<String, Collection<T>> getRenamedTypesForTesting() { Multimap<String, Collection<T>> ret = HashMultimap.create(); for (Map.Entry<String, Property> entry : properties.entrySet()) { Property prop = entry.getValue(); if (!prop.skipRenaming) { for (Collection<T> c : prop.getTypes().allEquivalenceClasses()) { if (!c.isEmpty() && !prop.typesToSkip.contains(c.iterator().next())) { ret.put(entry.getKey(), c); } } } } return ret; } /** Interface for providing the type information needed by this pass. */ private interface TypeSystem<T> { // TODO(user): add a getUniqueName(T type) method that is guaranteed // to be unique, performant and human-readable. /** Returns the top-most scope used by the type system (if any). */ StaticScope<T> getRootScope(); /** Returns the new scope started at the given function node. */ StaticScope<T> getFunctionScope(Node node); /** * Returns the type of the given node. * @param prop Only types with this property need to be returned. In general * with type tightening, this will require no special processing, but in * the case of an unknown JSType, we might need to add in the native * types since we don't track

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> them, but only if they have the given * property. */ T getType(StaticScope<T> scope, Node node, String prop); /** * Returns true if a field reference on this type will invalidate all * references to that field as candidates for renaming. This is true if the * type is unknown or all-inclusive, as variables with such a type could be * references to any object. */ boolean isInvalidatingType(T type); /** * Informs the given type system that a type is invalidating due to a type * mismatch found during type checking. */ void addInvalidatingType(JSType type); /** * Returns a set of types that should be skipped given the given type. * This is necessary for interfaces when using JSTypes, as all super * interfaces must also be skipped. */ ImmutableSet<T> getTypesToSkipForType(T type); /** * Determines whether the given type is one whose properties should not be * considered for renaming. */ boolean isTypeToSkip(T type); /** Remove null and undefined from the options in the given type. */ T restrictByNotNullOrUndefined(T type); /** * Returns the alternatives if this is a type that represents multiple * types, and null if not. Union and interface types can correspond to * multiple other types. */ Iterable<T> getTypeAlternatives(T type); /** * Returns the type in the chain from the given type that contains the given * field or null if it is not found anywhere. */ T getTypeWithProperty(String field, T type); /** * Returns the type of the instance of which this is the prototype or null * if this is not a function prototype. */ T getInstanceFromPrototype(T type); /** * Records that this property could be referenced from any interface that * this type, or any type in its superclass chain, implements. */ void recordInterfaces(T type, T relatedType, DisambiguateProperties<T>.Property p); } /** Implementation of TypeSystem using JSTypes. */ private static class JSTypeSystem implements TypeSystem<JSType> { private final Set<JSType> invalidatingTypes; private JSTypeRegistry registry; public JSTypeSystem(AbstractCompiler compiler) { registry = compiler.getTypeRegistry(); invalidatingTypes = Sets.newHashSet( registry.getNativeType(JSTypeNative.ALL_TYPE), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope<JSType> getRootScope() { return null; } @Override public StaticScope<JSType>

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> getFunctionScope(Node node) { return null; } @Override public JSType getType( StaticScope<JSType> scope, Node node, String prop) { if (node.getJSType() == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return node.getJSType(); } @Override public boolean isInvalidatingType(JSType type) { if (type == null || invalidatingTypes.contains(type) || type.isUnknownType() /* unresolved types */) { return true; } ObjectType objType = ObjectType.cast(type); return objType != null && !objType.hasReferenceName(); } @Override public ImmutableSet<JSType> getTypesToSkipForType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { Set<JSType> types = Sets.newHashSet(type); for (JSType alt : type.toMaybeUnionType().getAlternates()) { types.addAll(getTypesToSkipForTypeNonUnion(alt)); } return ImmutableSet.copyOf(types); } else if (type.isEnumElementType()) { return getTypesToSkipForType( type.toMaybeEnumElementType().getPrimitiveType()); } return ImmutableSet.copyOf(getTypesToSkipForTypeNonUnion(type)); } private Set<JSType> getTypesToSkipForTypeNonUnion(JSType type) { Set<JSType> types = Sets.newHashSet(); JSType skipType = type; while (skipType != null) { types.add(skipType); ObjectType objSkipType = skipType.toObjectType(); if (objSkipType != null) { skipType = objSkipType.getImplicitPrototype(); } else { break; } } return types; } @Override public boolean isTypeToSkip(JSType type) { return type.isEnumType() || (type.autoboxesTo() != null); } @Override public JSType restrictByNotNullOrUndefined(JSType type) { return type.restrictByNotNullOrUndefined(); } @Override public Iterable<JSType> getTypeAlternatives(JSType type) { if (type.isUnionType()) { return type.toMaybeUnionType().getAlternates(); } else { ObjectType objType = type.toObjectType(); if (objType != null && objType.getConstructor() != null && objType.getConstructor().isInterface()) { List<JSType> list = Lists.newArrayList(); for (FunctionType impl : registry.getDirectImplementors(objType)) { list.add(impl.getInstanceType()); } return list; } else { return null; } } } @Override public ObjectType getTypeWithProperty(String field, JSType type) { if (type == null) { return null; } if (type.isEnumElementType()) { return getTypeWithProperty( field, type.toMaybeEnumElementType().getPrimitiveType()); } if (!(type instanceof ObjectType)) { if (type.autoboxesTo() != null) { type = type.autoboxesTo(); } else {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> return null; } } // Ignore the prototype itself at all times. if ("prototype".equals(field)) { return null; } // We look up the prototype chain to find the highest place (if any) that // this appears. This will make references to overridden properties look // like references to the initial property, so they are renamed alike. ObjectType foundType = null; ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getConstructor() != null && objType.getConstructor().isInterface()) { ObjectType topInterface = FunctionType.getTopDefiningInterface( objType, field); if (topInterface != null && topInterface.getConstructor() != null) { foundType = topInterface.getConstructor().getPrototype(); } } else { while (objType != null && objType.getImplicitPrototype() != objType) { if (objType.hasOwnProperty(field)) { foundType = objType; } objType = objType.getImplicitPrototype(); } } // If the property does not exist on the referenced type but the original // type is an object type, see if any subtype has the property. if (foundType == null) { ObjectType maybeType = ObjectType.cast( registry.getGreatestSubtypeWithProperty(type, field)); // getGreatestSubtypeWithProperty does not guarantee that the property // is defined on the returned type, it just indicates that it might be, // so we have to double check. if (maybeType != null && maybeType.hasOwnProperty(field)) { foundType = maybeType; } } return foundType; } @Override public JSType getInstanceFromPrototype(JSType type) { if (type.isFunctionPrototypeType()) { ObjectType prototype = (ObjectType) type; FunctionType owner = prototype.getOwnerFunction(); if (owner.isConstructor() || owner.isInterface()) { return prototype.getOwnerFunction().getInstanceType(); } } return null; } @Override public void recordInterfaces(JSType type, JSType relatedType, DisambiguateProperties<JSType>.Property p) { ObjectType objType = ObjectType.cast(type); if (objType != null) { FunctionType constructor; if (objType.isFunctionType()) { constructor = objType.toMaybeFunctionType(); } else if (objType.isFunctionPrototypeType()) { constructor = objType.getOwnerFunction(); } else { constructor = objType.getConstructor(); } while (constructor != null) { for (ObjectType itype : constructor.getImplementedInterfaces()) { JSType top = getTypeWithProperty(p.name, itype); if (top != null) { p.addType(itype, top, relatedType); } else { recordInterfaces(itype, relatedType, p); } // If this interface invalidated this property, return now. if (p.skipRenaming) { return; } } if (constructor.isInterface() || constructor.isConstructor()) { constructor = constructor.getSuperClassConstructor(); } else { constructor = null; } } } } } /** Implementation of TypeSystem using

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> concrete types. */ private static class ConcreteTypeSystem implements TypeSystem<ConcreteType> { private final TightenTypes tt; private int nextUniqueId; private CodingConvention codingConvention; private final Set<JSType> invalidatingTypes = Sets.newHashSet(); // An array of native types that are not tracked by type tightening, and // thus need to be added in if an unknown type is encountered. private static final JSTypeNative [] nativeTypes = new JSTypeNative[] { JSTypeNative.BOOLEAN_OBJECT_TYPE, JSTypeNative.NUMBER_OBJECT_TYPE, JSTypeNative.STRING_OBJECT_TYPE }; public ConcreteTypeSystem(TightenTypes tt, CodingConvention convention) { this.tt = tt; this.codingConvention = convention; } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope<ConcreteType> getRootScope() { return tt.getTopScope(); } @Override public StaticScope<ConcreteType> getFunctionScope(Node decl) { ConcreteFunctionType func = tt.getConcreteFunction(decl); return (func != null) ? func.getScope() : (StaticScope<ConcreteType>) null; } @Override public ConcreteType getType( StaticScope<ConcreteType> scope, Node node, String prop) { if (scope != null) { ConcreteType c = tt.inferConcreteType( (TightenTypes.ConcreteScope) scope, node); return maybeAddAutoboxes(c, node, prop); } else { return null; } } /** * Add concrete types for autoboxing types if necessary. The concrete type * system does not track native types, like string, so add them if they are * present in the JSType for the node. */ private ConcreteType maybeAddAutoboxes( ConcreteType cType, Node node, String prop) { JSType jsType = node.getJSType(); if (jsType == null) { return cType; } else if (jsType.isUnknownType()) { for (JSTypeNative nativeType : nativeTypes) { ConcreteType concrete = tt.getConcreteInstance( tt.getTypeRegistry().getNativeObjectType(nativeType)); if (concrete != null && !concrete.getPropertyType(prop).isNone()) { cType = cType.unionWith(concrete); } } return cType; } return maybeAddAutoboxes(cType, jsType, prop); } private ConcreteType maybeAddAutoboxes( ConcreteType cType, JSType jsType, String prop) { jsType = jsType.restrictByNotNullOrUndefined(); if (jsType.isUnionType()) { for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { cType = maybeAddAutoboxes(cType, alt, prop); } return cType; } else if (jsType.isEnumElementType()) { return maybeAddAutoboxes( cType, jsType.toMaybeEnumElementType().getPrimitiveType(), prop); } if (jsType.autoboxesTo() != null) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> JSType autoboxed = jsType.autoboxesTo(); return cType.unionWith(tt.getConcreteInstance((ObjectType) autoboxed)); } else if (jsType.unboxesTo() != null) { return cType.unionWith(tt.getConcreteInstance((ObjectType) jsType)); } return cType; } @Override public boolean isInvalidatingType(ConcreteType type) { // We will disallow types on functions so that 'prototype' is not renamed. // TODO(user): Support properties on functions as well. return (type == null) || type.isAll() || type.isFunction() || (type.isInstance() && invalidatingTypes.contains(type.toInstance().instanceType)); } @Override public ImmutableSet<ConcreteType> getTypesToSkipForType(ConcreteType type) { return ImmutableSet.of(type); } @Override public boolean isTypeToSkip(ConcreteType type) { // Skip anonymous object literals and enum types. return type.isInstance() && !(type.toInstance().isFunctionPrototype() || type.toInstance().instanceType.isInstanceType()); } @Override public ConcreteType restrictByNotNullOrUndefined(ConcreteType type) { // These are not represented in concrete types. return type; } @Override public Iterable<ConcreteType> getTypeAlternatives(ConcreteType type) { if (type.isUnion()) { return ((ConcreteUnionType) type).getAlternatives(); } else { return null; } } @Override public ConcreteType getTypeWithProperty(String field, ConcreteType type) { if (type.isInstance()) { ConcreteInstanceType instanceType = (ConcreteInstanceType) type; return instanceType.getInstanceTypeWithProperty(field); } else if (type.isFunction()) { if ("prototype".equals(field) || codingConvention.isSuperClassReference(field)) { return type; } } else if (type.isNone()) { // If the receiver is none, then this code is never reached. We will // return a new fake type to ensure that this access is renamed // differently from any other, so it can be easily removed. return new ConcreteUniqueType(++nextUniqueId); } else if (type.isUnion()) { // If only one has the property, return that. for (ConcreteType t : ((ConcreteUnionType) type).getAlternatives()) { ConcreteType ret = getTypeWithProperty(field, t); if (ret != null) { return ret; } } } return null; } @Override public ConcreteType getInstanceFromPrototype(ConcreteType type) { if (type.isInstance()) { ConcreteInstanceType instanceType = (ConcreteInstanceType) type; if (instanceType.isFunctionPrototype()) { return instanceType.getConstructorType().getInstanceType(); } } return null; } @Override public void recordInterfaces(ConcreteType type, ConcreteType relatedType, DisambiguateProperties<ConcreteType>.Property p) { // No need to record interfaces when using concrete types. } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; /** * The error manager is in charge of storing, organizing and displaying * errors and warnings generated by the compiler. * */ public interface ErrorManager extends ErrorHandler { /** * Reports an error. The errors will be displayed by the * {@link #generateReport()} at the discretion of the implementation. * * @param level the reporting level * @param error the error to report */ @Override void report(CheckLevel level, JSError error); /** * Writes a report to an implementation-specific medium. The compiler calls * this method after any and all {@link #report} calls. */ void generateReport(); /** * Gets the number of reported errors. */ int getErrorCount(); /** * Gets the number of reported warnings. */ int getWarningCount(); /** * Gets all the errors. */ JSError[] getErrors(); /** * Gets all the warnings. */ JSError[] getWarnings(); /** * Sets the percentage of typed expressions. */ void setTypedPercent(double typedPercent); /** * Gets the percentage of typed expressions. */ double getTypedPercent(); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Check for usage of 'with'. * */ class ControlStructureCheck implements HotSwapCompilerPass { private final AbstractCompiler compiler; static final DiagnosticType USE_OF_WITH = DiagnosticType.warning( "JSC_USE_OF_WITH", "The use of the 'with' structure should be avoided."); ControlStructureCheck(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { check(root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { check(scriptRoot); } /** * Reports errors for any invalid use of control structures. * * @param node Current node to check. */ private void check(Node node) { switch (node.getType()) { case Token.WITH: JSDocInfo info = node.getJSDocInfo(); boolean allowWith = info != null && info.getSuppressions().contains("with"); if (!allowWith) { report(node, USE_OF_WITH); } break; } for (Node bChild = node.getFirstChild(); bChild != null;) { Node next = bChild.getNext(); check(bChild); bChild = next; } } private void report(Node n, DiagnosticType error) { compiler.report(JSError.make(n.getSourceFileName(), n, error)); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.util.Collections; /** * An object type which uses composition to delegate all calls. * * @see NamedType * @see TemplatizedType * */ class ProxyObjectType extends ObjectType { private static final long serialVersionUID = 1L; private JSType referencedType; private ObjectType referencedObjType; ProxyObjectType(JSTypeRegistry registry, JSType referencedType) { this(registry, referencedType, null); } ProxyObjectType(JSTypeRegistry registry, JSType referencedType, TemplateTypeMap templateTypeMap) { super(registry, templateTypeMap); setReferencedType(referencedType); } @Override PropertyMap getPropertyMap() { return referencedObjType == null ? PropertyMap.immutableEmptyMap() : referencedObjType.getPropertyMap(); } JSType getReferencedTypeInternal() { return referencedType; } ObjectType getReferencedObjTypeInternal() { return referencedObjType; } void setReferencedType(JSType referencedType) { this.referencedType = referencedType; if (referencedType instanceof ObjectType) { this.referencedObjType = (ObjectType) referencedType; } else { this.referenced

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>ObjType = null; } } @Override public String getReferenceName() { return referencedObjType == null ? "" : referencedObjType.getReferenceName(); } @Override public boolean hasReferenceName() { return referencedObjType == null ? null : referencedObjType.hasReferenceName(); } @Override public boolean matchesNumberContext() { return referencedType.matchesNumberContext(); } @Override public boolean matchesStringContext() { return referencedType.matchesStringContext(); } @Override public boolean matchesObjectContext() { return referencedType.matchesObjectContext(); } @Override public boolean canBeCalled() { return referencedType.canBeCalled(); } @Override public boolean isNoType() { return referencedType.isNoType(); } @Override public boolean isNoObjectType() { return referencedType.isNoObjectType(); } @Override public boolean isNoResolvedType() { return referencedType.isNoResolvedType(); } @Override public boolean isUnknownType() { return referencedType.isUnknownType(); } @Override public boolean isCheckedUnknownType() { return referencedType.isCheckedUnknownType(); } @Override public boolean isNullable() { return referencedType.isNullable(); } @Override public EnumType toMaybeEnumType() { return referencedType.toMaybeEnumType(); } @Override public boolean isConstructor() { return referencedType.isConstructor(); } @Override public boolean isNominalType() { return referencedType.isNominalType(); } @Override public boolean isInstanceType() { return referencedType.isInstanceType(); } @Override public boolean isInterface() { return referencedType.isInterface(); } @Override public boolean isOrdinaryFunction() { return referencedType.isOrdinaryFunction(); } @Override public boolean isAllType() { return referencedType.isAllType(); } @Override public boolean isStruct() { return referencedType.isStruct(); } @Override public boolean isDict() { return referencedType.isDict(); } @Override public boolean isNativeObjectType() { return referencedObjType == null ? false : referencedObjType.isNativeObjectType(); } @Override RecordType toMaybeRecordType() { return referencedType.toMaybeRecordType(); } @Override public UnionType toMaybeUnionType() { return referencedType.toMaybeUnionType(); } @Override public FunctionType toMaybeFunctionType() { return referencedType.toMaybeFunctionType(); } @Override public EnumElementType toMaybeEnumElementType() { return referencedType.toMaybeEnumElementType(); } @Override public TernaryValue testForEquality(JSType that) { return referencedType.testForEquality(that); } @Override public boolean isSubtype(JSType that) { return referencedType.isSubtype(that); } @Override public FunctionType getOwnerFunction() { return referencedObjType == null ? null : referencedObjType.getOwnerFunction(); } @Override public Iterable<ObjectType> getCtorImplementedInterfaces() { return referencedObjType == null ? Collections.<ObjectType>emptyList() : referencedObjType

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.getCtorImplementedInterfaces(); } @Override public int hashCode() { return referencedType.hashCode(); } @Override String toStringHelper(boolean forAnnotations) { return referencedType.toStringHelper(forAnnotations); } @Override public ObjectType getImplicitPrototype() { return referencedObjType == null ? null : referencedObjType.getImplicitPrototype(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { return referencedObjType == null ? true : referencedObjType.defineProperty( propertyName, type, inferred, propertyNode); } @Override public boolean removeProperty(String name) { return referencedObjType == null ? false : referencedObjType.removeProperty(name); } @Override public JSType findPropertyType(String propertyName) { return referencedType.findPropertyType(propertyName); } @Override public JSDocInfo getJSDocInfo() { return referencedType.getJSDocInfo(); } @Override public void setJSDocInfo(JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setJSDocInfo(info); } } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setPropertyJSDocInfo(propertyName, info); } } @Override public FunctionType getConstructor() { return referencedObjType == null ? null : referencedObjType.getConstructor(); } @Override public ImmutableList<JSType> getTemplateTypes() { return referencedObjType == null ? null : referencedObjType.getTemplateTypes(); } @Override public <T> T visit(Visitor<T> visitor) { return referencedType.visit(visitor); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return referencedType.visit(visitor, that); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setReferencedType(referencedType.resolve(t, scope)); return this; } @Override public String toDebugHashCodeString() { return "{proxy:" + referencedType.toDebugHashCodeString() + "}"; } @Override public JSType getTypeOfThis() { if (referencedObjType != null) { return referencedObjType.getTypeOfThis(); } return super.getTypeOfThis(); } @Override public JSType collapseUnion() { if (referencedType.isUnionType()) { return referencedType.collapseUnion(); } return this; } @Override public void matchConstraint(JSType constraint) { referencedType.matchConstraint(constraint); } @Override public TemplatizedType toMaybeTemplatizedType() { return referencedType.toMaybeTemplatizedType(); } @Override public TemplateType toMaybeTemplateType() { return referencedType.toMaybeTemplateType(); } @Override public boolean hasAnyTemplateTypesInternal() { return referencedType.hasAnyTemplateTypes(); } @Override public TemplateTypeMap getTemplateTypeMap() { return referencedType.getTemplateTypeMap(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>_DEPRECATED_CLASS_REASON", "Class {0} has been deprecated: {1}"); static final DiagnosticType BAD_PRIVATE_GLOBAL_ACCESS = DiagnosticType.disabled( "JSC_BAD_PRIVATE_GLOBAL_ACCESS", "Access to private variable {0} not allowed outside file {1}."); static final DiagnosticType BAD_PRIVATE_PROPERTY_ACCESS = DiagnosticType.disabled( "JSC_BAD_PRIVATE_PROPERTY_ACCESS", "Access to private property {0} of {1} not allowed here."); static final DiagnosticType BAD_PROTECTED_PROPERTY_ACCESS = DiagnosticType.disabled( "JSC_BAD_PROTECTED_PROPERTY_ACCESS", "Access to protected property {0} of {1} not allowed here."); static final DiagnosticType PRIVATE_OVERRIDE = DiagnosticType.disabled( "JSC_PRIVATE_OVERRIDE", "Overriding private property of {0}."); static final DiagnosticType EXTEND_FINAL_CLASS = DiagnosticType.error( "JSC_EXTEND_FINAL_CLASS", "{0} is not allowed to extend final class {1}."); static final DiagnosticType VISIBILITY_MISMATCH = DiagnosticType.disabled( "JSC_VISIBILITY_MISMATCH", "Overriding {0} property of {1} with {2} property."); static final DiagnosticType CONST_PROPERTY_REASSIGNED_VALUE = DiagnosticType.warning( "JSC_CONSTANT_PROPERTY_REASSIGNED_VALUE", "constant property {0} assigned a value more than once"); static final DiagnosticType CONST_PROPERTY_DELETED = DiagnosticType.warning( "JSC_CONSTANT_PROPERTY_DELETED", "constant property {0} cannot be deleted"); private final AbstractCompiler compiler; private final TypeValidator validator; // State about the current traversal. private int deprecatedDepth = 0; private int methodDepth = 0; private JSType currentClass = null; private final Multimap<String, String> initializedConstantProperties; CheckAccessControls(AbstractCompiler compiler) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.initializedConstantProperties = HashMultimap.create(); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n)) { deprecatedDepth++; } if (methodDepth == 0) { currentClass = getClassOfMethod(n, parent); } methodDepth++; } } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); if (isDeprecatedFunction(n)) { deprecatedDepth--; } methodDepth--; if (methodDepth == 0) { currentClass = null; } } } /** * Gets the type of the class

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> that "owns" a method, or null if * we know that its un-owned. */ private JSType getClassOfMethod(Node n, Node parent) { if (parent.isAssign()) { Node lValue = parent.getFirstChild(); if (NodeUtil.isGet(lValue)) { // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType != null && lValueType.isNominalConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return (lValueType.toMaybeFunctionType()).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } } else if (NodeUtil.isFunctionDeclaration(n) || parent.isName()) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isNominalConstructor()) { return (type.toMaybeFunctionType()).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((ObjectType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); checkConstantProperty(t, n); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; case Token.FUNCTION: checkFinalClassOverrides(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo));

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; /** * An object type with declared template types, such as * <code>Array.<string></code>. * */ public final class TemplatizedType extends ProxyObjectType { private static final long serialVersionUID = 1L; final ImmutableList<JSType> templateTypes; final TemplateTypeMapReplacer replacer; TemplatizedType( JSTypeRegistry registry, ObjectType objectType, ImmutableList<JSType> templateTypes) { super(registry, objectType, objectType.getTemplateTypeMap().addValues( templateTypes)); // Cache which template keys were filled, and what JSTypes they were filled // with. ImmutableList<TemplateType> filledTemplateKeys = objectType.getTemplateTypeMap().getUnfilledTemplateKeys(); ImmutableList.Builder<JSType> builder = ImmutableList.builder(); for (TemplateType filledTemplateKey : filledTemplateKeys) { builder.add(getTemplateTypeMap().getTemplateType(filledTemplateKey)); } this.templateTypes = builder.build(); replacer = new TemplateTypeMapReplacer(registry, getTemplateTypeMap()); } @Override String toStringHelper(boolean forAnnotations) { String typeString = super.toStringHelper(forAnnotations); if (!templateTypes.isEmpty()) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> typeString += ".<" + Joiner.on(",").join(templateTypes) + ">"; } return typeString; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseTemplatizedType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseTemplatizedType(this, that); } @Override public TemplatizedType toMaybeTemplatizedType() { return this; } @Override public ImmutableList<JSType> getTemplateTypes() { return templateTypes; } @Override public JSType getPropertyType(String propertyName) { JSType result = super.getPropertyType(propertyName); return result == null ? null : result.visit(replacer); } @Override public boolean isSubtype(JSType that) { return isSubtypeHelper(this, that); } boolean wrapsSameRawType(JSType that) { return that.isTemplatizedType() && this.getReferencedTypeInternal() .isEquivalentTo( that.toMaybeTemplatizedType().getReferencedTypeInternal()); } boolean wrapsRawType(JSType that) { return this.getReferencedTypeInternal().isEquivalentTo(that); } /** * Computes the greatest subtype of two related templatized types. * @return The greatest subtype. */ JSType getGreatestSubtypeHelper(JSType rawThat) { Preconditions.checkNotNull(rawThat); if (!wrapsSameRawType(rawThat)) { if (!rawThat.isTemplatizedType()) { if (this.isSubtype(rawThat)) { return this; } else if (rawThat.isSubtype(this)) { return filterNoResolvedType(rawThat); } } if (this.isObject() && rawThat.isObject()) { return this.getNativeType(JSTypeNative.NO_OBJECT_TYPE); } return this.getNativeType(JSTypeNative.NO_TYPE); } TemplatizedType that = rawThat.toMaybeTemplatizedType(); Preconditions.checkNotNull(that); if (getTemplateTypeMap().checkEquivalenceHelper( that.getTemplateTypeMap(), EquivalenceMethod.INVARIANT)) { return this; } // For types that have the same raw type but different type parameters, // we simply create a type has a "unknown" type parameter. This is // equivalent to the raw type. return getReferencedObjTypeInternal(); } @Override public TemplateTypeMap getTemplateTypeMap() { return templateTypeMap; } @Override public boolean hasAnyTemplateTypesInternal() { return templateTypeMap.hasAnyTemplateTypesInternal(); } /** * @return The referenced ObjectType wrapped by this TemplatizedType. */ public ObjectType getReferencedType() { return getReferencedObjTypeInternal(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> be used together."); static final DiagnosticType CANNOT_USE_EXPORT_LOCALS_AND_EXTERN_PROP_REMOVAL = DiagnosticType.error("JSC_CANNOT_USE_EXPORT_LOCALS_AND_EXTERN_PROP_REMOVAL", "remove_unused_prototype_properties_in_externs " + "and export_local_property_definitions cannot be used together."); // Miscellaneous errors. static final DiagnosticType REPORT_PATH_IO_ERROR = DiagnosticType.error("JSC_REPORT_PATH_IO_ERROR", "Error writing compiler report to {0}"); private static final DiagnosticType NAME_REF_GRAPH_FILE_ERROR = DiagnosticType.error("JSC_NAME_REF_GRAPH_FILE_ERROR", "Error \"{1}\" writing name reference graph to \"{0}\"."); private static final DiagnosticType NAME_REF_REPORT_FILE_ERROR = DiagnosticType.error("JSC_NAME_REF_REPORT_FILE_ERROR", "Error \"{1}\" writing name reference report to \"{0}\"."); private static final java.util.regex.Pattern GLOBAL_SYMBOL_NAMESPACE_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9$_]+$"); /** * A global namespace to share across checking passes. */ private GlobalNamespace namespaceForChecks = null; /** * A symbol table for registering references that get removed during * preprocessing. */ private PreprocessorSymbolTable preprocessorSymbolTable = null; /** * A type-tightener to share across optimization passes. */ private TightenTypes tightenTypes = null; /** Names exported by goog.exportSymbol. */ private Set<String> exportedNames = null; /** Shared name generator that remembers character encoding bias */ private NameGenerator nameGenerator = null; /** * Ids for cross-module method stubbing, so that each method has * a unique id. */ private CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator = new CrossModuleMethodMotion.IdGenerator(); /** * Keys are arguments passed to getCssName() found during compilation; values * are the number of times the key appeared as an argument to getCssName(). */ private Map<String, Integer> cssNames = null; /** The variable renaming map */ private VariableMap variableMap = null; /** The property renaming map */ private VariableMap propertyMap = null; /** The naming map for anonymous functions */ private VariableMap anonymousFunctionNameMap = null; /** Fully qualified function names and globally unique ids */ private FunctionNames functionNames = null; /** String replacement map */ private VariableMap stringMap = null; /** Id generator map */ private String idGeneratorMap = null; public DefaultPassConfig(CompilerOptions options) { super(options); } @Override protected State getIntermediateState() { return new State( cssNames == null ? null : Maps.newHashMap(cssNames), exportedNames == null ? null : Collections.unmodifiableSet(exportedNames), crossModuleIdGenerator, variableMap, propertyMap, anonymousFunctionNameMap, stringMap, functionNames, idGeneratorMap); } @Override protected void setIntermediateState(State state) { this.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>cssNames = state.cssNames == null ? null : Maps.newHashMap(state.cssNames); this.exportedNames = state.exportedNames == null ? null : Sets.newHashSet(state.exportedNames); this.crossModuleIdGenerator = state.crossModuleIdGenerator; this.variableMap = state.variableMap; this.propertyMap = state.propertyMap; this.anonymousFunctionNameMap = state.anonymousFunctionNameMap; this.stringMap = state.stringMap; this.functionNames = state.functionNames; this.idGeneratorMap = state.idGeneratorMap; } GlobalNamespace getGlobalNamespace() { return namespaceForChecks; } PreprocessorSymbolTable getPreprocessorSymbolTable() { return preprocessorSymbolTable; } private NameGenerator getNameGenerator() { if (nameGenerator == null) { nameGenerator = new NameGenerator(new HashSet<String>(0), "", null); } return nameGenerator; } void maybeInitializePreprocessorSymbolTable(AbstractCompiler compiler) { if (options.ideMode) { Node root = compiler.getRoot(); if (preprocessorSymbolTable == null || preprocessorSymbolTable.getRootNode() != root) { preprocessorSymbolTable = new PreprocessorSymbolTable(root); } } } @Override protected List<PassFactory> getChecks() { List<PassFactory> checks = Lists.newArrayList(); checks.add(createEmptyPass("beforeStandardChecks")); if (options.closurePass) { checks.add(closureGoogScopeAliases); checks.add(closureRewriteGoogClass); } if (options.nameAnonymousFunctionsOnly) { if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.MAPPED) { checks.add(nameMappedAnonymousFunctions); } else if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.UNMAPPED) { checks.add(nameUnmappedAnonymousFunctions); } return checks; } if (options.jqueryPass) { checks.add(jqueryAliases); } if (options.angularPass) { checks.add(angularPass); } checks.add(checkSideEffects); if (options.checkSuspiciousCode || options.enables(DiagnosticGroups.GLOBAL_THIS) || options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { checks.add(suspiciousCode); } if (options.checkControlStructures || options.enables(DiagnosticGroups.ES5_STRICT)) { checks.add(checkControlStructures); } if (options.checkRequires.isOn()) { checks.add(checkRequires); } if (options.checkProvides.isOn()) { checks.add(checkProvides); } // The following passes are more like "preprocessor" passes. // It's important that they run before most checking passes. // Perhaps this method should be renamed? if (options.generateExports) { checks.add(generateExports); } if (options.exportTestFunctions) { checks.add(exportTestFunctions); } if (options.closurePass) { checks.add(closurePrimitives); } if (options.closurePass && options

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.nameReferenceReportPath != null && !options.nameReferenceReportPath.isEmpty()) { checks.add(printNameReferenceReport); } checks.add(createEmptyPass("afterStandardChecks")); assertAllOneTimePasses(checks); return checks; } @Override protected List<PassFactory> getOptimizations() { List<PassFactory> passes = Lists.newArrayList(); passes.add(garbageCollectChecks); // TODO(nicksantos): The order of these passes makes no sense, and needs // to be re-arranged. if (options.instrumentForCoverage) { passes.add(instrumentForCodeCoverage); } if (options.runtimeTypeCheck) { passes.add(runtimeTypeCheck); } passes.add(createEmptyPass("beforeStandardOptimizations")); if (options.replaceIdGenerators) { passes.add(replaceIdGenerators); } // Optimizes references to the arguments variable. if (options.optimizeArgumentsArray) { passes.add(optimizeArgumentsArray); } // Abstract method removal works best on minimally modified code, and also // only needs to run once. if (options.closurePass && (options.removeAbstractMethods || options.removeClosureAsserts)) { passes.add(closureCodeRemoval); } // Property disambiguation should only run once and needs to be done // soon after type checking, both so that it can make use of type // information and so that other passes can take advantage of the renamed // properties. if (options.disambiguatePrivateProperties) { passes.add(disambiguatePrivateProperties); } // Collapsing properties can undo constant inlining, so we do this before // the main optimization loop. if (options.collapseProperties) { passes.add(collapseProperties); } // ReplaceStrings runs after CollapseProperties in order to simplify // pulling in values of constants defined in enums structures. if (!options.replaceStringsFunctionDescriptions.isEmpty()) { passes.add(replaceStrings); } // Tighten types based on actual usage. if (options.tightenTypes) { passes.add(tightenTypesBuilder); } // Property disambiguation should only run once and needs to be done // soon after type checking, both so that it can make use of type // information and so that other passes can take advantage of the renamed // properties. if (options.disambiguateProperties) { passes.add(disambiguateProperties); } if (options.computeFunctionSideEffects) { passes.add(markPureFunctions); } else if (options.markNoSideEffectCalls) { // TODO(user) The properties that this pass adds to CALL and NEW // AST nodes increase the AST's in-memory size. Given that we are // already running close to our memory limits, we could run into // trouble if we end up using the @nosideeffects annotation a lot // or compute @nosideeffects annotations by looking at function // bodies. It should be easy to propagate @nosideeffects // annotations as part of passes that depend on this property and // store the result outside the AST (which would allow garbage // collection once the pass is

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> (options.deadAssignmentElimination) { passes.add(deadAssignmentsElimination); } if (!runOptimizeCalls) { passes.add(removeUnusedVars); } } if (runOptimizeCalls) { passes.add(optimizeCallsAndRemoveUnusedVars); } assertAllLoopablePasses(passes); return passes; } /** Creates several passes aimed at removing code. */ private List<PassFactory> getCodeRemovingPasses() { List<PassFactory> passes = Lists.newArrayList(); if (options.collapseObjectLiterals && !isInliningForbidden()) { passes.add(collapseObjectLiterals); } if (options.inlineVariables || options.inlineLocalVariables) { passes.add(inlineVariables); } else if (options.inlineConstantVars) { passes.add(inlineConstants); } if (options.foldConstants) { // These used to be one pass. passes.add(minimizeExitPoints); passes.add(peepholeOptimizations); } if (options.removeDeadCode) { passes.add(removeUnreachableCode); } if (options.removeUnusedPrototypeProperties) { passes.add(removeUnusedPrototypeProperties); } if (options.removeUnusedClassProperties && !isInliningForbidden()) { passes.add(removeUnusedClassProperties); } assertAllLoopablePasses(passes); return passes; } /** * Checks for code that is probably wrong (such as stray expressions). */ final HotSwapPassFactory checkSideEffects = new HotSwapPassFactory("checkSideEffects", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { // The current approach to protecting "hidden" side-effects is to // wrap them in a function call that is stripped later, this shouldn't // be done in IDE mode where AST changes may be unexpected. boolean protectHiddenSideEffects = options.protectHiddenSideEffects && !options.ideMode; return new CheckSideEffects(compiler, options.checkSuspiciousCode ? CheckLevel.WARNING : CheckLevel.OFF, protectHiddenSideEffects); } }; /** * Checks for code that is probably wrong (such as stray expressions). */ final PassFactory stripSideEffectProtection = new PassFactory("stripSideEffectProtection", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CheckSideEffects.StripProtection(compiler); } }; /** * Checks for code that is probably wrong (such as stray expressions). */ final HotSwapPassFactory suspiciousCode = new HotSwapPassFactory("suspiciousCode", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { List<Callback> sharedCallbacks = Lists.newArrayList(); if (options.checkSuspiciousCode) { sharedCallbacks.add(new CheckSuspiciousCode()); } if (options.enables(DiagnosticGroups.GLOBAL_THIS)) { sharedCallbacks.add(new CheckGlobalThis(compiler)); } if (options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { sharedCallbacks.add(new CheckDebuggerStatement(compiler));

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } return combineChecks(compiler, sharedCallbacks); } }; /** Verify that all the passes are one-time passes. */ private static void assertAllOneTimePasses(List<PassFactory> passes) { for (PassFactory pass : passes) { Preconditions.checkState(pass.isOneTimePass()); } } /** Verify that all the passes are multi-run passes. */ private static void assertAllLoopablePasses(List<PassFactory> passes) { for (PassFactory pass : passes) { Preconditions.checkState(!pass.isOneTimePass()); } } /** Checks for validity of the control structures. */ final HotSwapPassFactory checkControlStructures = new HotSwapPassFactory("checkControlStructures", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new ControlStructureCheck(compiler); } }; /** Checks that all constructed classes are goog.require()d. */ final HotSwapPassFactory checkRequires = new HotSwapPassFactory("checkRequires", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new CheckRequiresForConstructors(compiler, options.checkRequires); } }; /** Makes sure @constructor is paired with goog.provides(). */ final HotSwapPassFactory checkProvides = new HotSwapPassFactory("checkProvides", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new CheckProvides(compiler, options.checkProvides); } }; private static final DiagnosticType GENERATE_EXPORTS_ERROR = DiagnosticType.error( "JSC_GENERATE_EXPORTS_ERROR", "Exports can only be generated if export symbol/property " + "functions are set."); /** Generates exports for @export annotations. */ final PassFactory generateExports = new PassFactory("generateExports", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (options.removeUnusedPrototypePropertiesInExterns && options.exportLocalPropertyDefinitions) { return new ErrorPass( compiler, CANNOT_USE_EXPORT_LOCALS_AND_EXTERN_PROP_REMOVAL); } CodingConvention convention = compiler.getCodingConvention(); if (convention.getExportSymbolFunction() != null && convention.getExportPropertyFunction() != null) { return new GenerateExports(compiler, options.exportLocalPropertyDefinitions, convention.getExportSymbolFunction(), convention.getExportPropertyFunction()); } else { return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); } } }; /** Generates exports for functions associated with JsUnit. */ final PassFactory exportTestFunctions = new PassFactory("exportTestFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { CodingConvention convention = compiler.getCodingConvention(); if (convention.getExportSymbolFunction() != null) { return new ExportTestFunctions(compiler, convention.getExportSymbolFunction(), convention.getExportPropertyFunction()); } else { return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); } } }; /** Raw exports processing pass. */ final PassFactory gatherRawExports = new PassFactory("

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>gatherRawExports", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { final GatherRawExports pass = new GatherRawExports( compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); if (exportedNames == null) { exportedNames = Sets.newHashSet(); } exportedNames.addAll(pass.getExportedVariableNames()); } }; } }; /** Closure pre-processing pass. */ @SuppressWarnings("deprecation") final HotSwapPassFactory closurePrimitives = new HotSwapPassFactory("closurePrimitives", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { maybeInitializePreprocessorSymbolTable(compiler); final ProcessClosurePrimitives pass = new ProcessClosurePrimitives( compiler, preprocessorSymbolTable, options.brokenClosureRequiresLevel); return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); exportedNames = pass.getExportedVariableNames(); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { pass.hotSwapScript(scriptRoot, originalRoot); } }; } }; /** Expand jQuery Primitives and Aliases pass. */ final PassFactory jqueryAliases = new PassFactory("jqueryAliases", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ExpandJqueryAliases(compiler); } }; /** Process AngularJS-specific annotations. */ final PassFactory angularPass = new PassFactory("angularPass", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AngularPass(compiler); } }; /** * The default i18n pass. * A lot of the options are not configurable, because ReplaceMessages * has a lot of legacy logic. */ final PassFactory replaceMessages = new PassFactory("replaceMessages", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ReplaceMessages(compiler, options.messageBundle, /* warn about message dupes */ true, /* allow messages with goog.getMsg */ JsMessage.Style.getFromParams(true, false), /* if we can't find a translation, don't worry about it. */ false); } }; final PassFactory replaceMessagesForChrome = new PassFactory("replaceMessages", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ReplaceMessagesForChrome(compiler, new GoogleJsMessageIdGenerator(options.tcProjectId), /* warn about message dupes */ true, /* allow messages with goog.getMsg */ JsMessage.Style.getFromParams(true, false)); } }; /** Applies aliases and inlines goog.scope. */ final HotSwapPassFactory closureGoogScopeAliases = new HotSwapPassFactory("closureGoogScopeAliases", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { maybeInitializePreprocessorSymbolTable(compiler); return new ScopedAliases( compiler, preprocessorSymbolTable, options.getAliasTransformation

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Handler()); } }; /** Rewrites goog.class */ final HotSwapPassFactory closureRewriteGoogClass = new HotSwapPassFactory("closureRewriteGoogClass", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new ClosureRewriteClass(compiler); } }; /** Checks that CSS class names are wrapped in goog.getCssName */ final PassFactory closureCheckGetCssName = new PassFactory("closureCheckGetCssName", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { String blacklist = options.checkMissingGetCssNameBlacklist; Preconditions.checkState(blacklist != null && !blacklist.isEmpty(), "Not checking use of goog.getCssName because of empty blacklist."); return new CheckMissingGetCssName( compiler, options.checkMissingGetCssNameLevel, blacklist); } }; /** * Processes goog.getCssName. The cssRenamingMap is used to lookup * replacement values for the classnames. If null, the raw class names are * inlined. */ final PassFactory closureReplaceGetCssName = new PassFactory("closureReplaceGetCssName", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { Map<String, Integer> newCssNames = null; if (options.gatherCssNames) { newCssNames = Maps.newHashMap(); } ReplaceCssNames pass = new ReplaceCssNames( compiler, newCssNames, options.cssRenamingWhitelist); pass.process(externs, jsRoot); cssNames = newCssNames; } }; } }; /** * Creates synthetic blocks to prevent FoldConstants from moving code * past markers in the source. */ final PassFactory createSyntheticBlocks = new PassFactory("createSyntheticBlocks", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CreateSyntheticBlocks(compiler, options.syntheticBlockStartMarker, options.syntheticBlockEndMarker); } }; /** Various peephole optimizations. */ final PassFactory peepholeOptimizations = new PassFactory("peepholeOptimizations", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { final boolean late = false; return new PeepholeOptimizationsPass(compiler, new PeepholeMinimizeConditions(late), new PeepholeSubstituteAlternateSyntax(late), new PeepholeReplaceKnownMethods(late), new PeepholeRemoveDeadCode(), new PeepholeFoldConstants(late), new PeepholeCollectPropertyAssignments()); } }; /** Same as peepholeOptimizations but aggressively merges code together */ final PassFactory latePeepholeOptimizations = new PassFactory("latePeepholeOptimizations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { final boolean late = true; return new PeepholeOptimizationsPass(compiler, new StatementFusion(options.aggressiveFusion), new P

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>eepholeRemoveDeadCode(), new PeepholeMinimizeConditions(late), new PeepholeSubstituteAlternateSyntax(late), new PeepholeReplaceKnownMethods(late), new PeepholeFoldConstants(late), new ReorderConstantExpression()); } }; /** Checks that all variables are defined. */ final HotSwapPassFactory checkVars = new HotSwapPassFactory("checkVars", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new VarCheck(compiler); } }; /** Checks for RegExp references. */ final PassFactory checkRegExp = new PassFactory("checkRegExp", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { final CheckRegExp pass = new CheckRegExp(compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); compiler.setHasRegExpGlobalReferences( pass.isGlobalRegExpPropertiesUsed()); } }; } }; /** Checks that references to variables look reasonable. */ final HotSwapPassFactory checkVariableReferences = new HotSwapPassFactory("checkVariableReferences", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new VariableReferenceCheck( compiler, options.aggressiveVarCheck); } }; /** Pre-process goog.testing.ObjectPropertyString. */ final PassFactory objectPropertyStringPreprocess = new PassFactory("ObjectPropertyStringPreprocess", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ObjectPropertyStringPreprocess(compiler); } }; /** Creates a typed scope and adds types to the type registry. */ final HotSwapPassFactory resolveTypes = new HotSwapPassFactory("resolveTypes", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new GlobalTypeResolver(compiler); } }; /** Clears the typed scope when we're done. */ final PassFactory clearTypedScopePass = new PassFactory("clearTypedScopePass", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ClearTypedScope(); } }; /** Runs type inference. */ final HotSwapPassFactory inferTypes = new HotSwapPassFactory("inferTypes", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); makeTypeInference(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeTypeInference(compiler).inferAllScopes(scriptRoot); } }; } }; final HotSwapPassFactory inferJsDocInfo = new HotSwapPassFactory("inferJsDocInfo", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>getTypedScopeCreator()); makeInferJsDocInfo(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot); } }; } }; /** Checks type usage */ final HotSwapPassFactory checkTypes = new HotSwapPassFactory("checkTypes", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); TypeCheck check = makeTypeCheck(compiler); check.process(externs, root); compiler.getErrorManager().setTypedPercent(check.getTypedPercent()); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeTypeCheck(compiler).check(scriptRoot, false); } }; } }; /** * Checks possible execution paths of the program for problems: missing return * statements and dead code. */ final HotSwapPassFactory checkControlFlow = new HotSwapPassFactory("checkControlFlow", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { List<Callback> callbacks = Lists.newArrayList(); if (options.checkUnreachableCode.isOn()) { callbacks.add( new CheckUnreachableCode(compiler, options.checkUnreachableCode)); } if (options.checkMissingReturn.isOn() && options.checkTypes) { callbacks.add( new CheckMissingReturn(compiler, options.checkMissingReturn)); } return combineChecks(compiler, callbacks); } }; /** Checks access controls. Depends on type-inference. */ final HotSwapPassFactory checkAccessControls = new HotSwapPassFactory("checkAccessControls", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new CheckAccessControls(compiler); } }; /** Executes the given callbacks with a {@link CombinedCompilerPass}. */ private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler, List<Callback> callbacks) { Preconditions.checkArgument(callbacks.size() > 0); Callback[] array = callbacks.toArray(new Callback[callbacks.size()]); return new CombinedCompilerPass(compiler, array); } /** A compiler pass that resolves types in the global scope. */ class GlobalTypeResolver implements HotSwapCompilerPass { private final AbstractCompiler compiler; GlobalTypeResolver(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (topScope == null) { regenerateGlobalTypedScope(compiler, root.getParent()); } else { compiler.getTypeRegistry().resolveTypesInScope(topScope); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { patchGlobalTypedScope(compiler, scriptRoot); } } /** A compiler pass that clears the global scope. */ class ClearTypedScope implements CompilerPass { @Override public void process(Node

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> externs, Node root) { clearTypedScope(); } } /** Checks global name usage. */ final PassFactory checkGlobalNames = new PassFactory("checkGlobalNames", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { // Create a global namespace for analysis by check passes. // Note that this class does all heavy computation lazily, // so it's OK to create it here. namespaceForChecks = new GlobalNamespace(compiler, externs, jsRoot); new CheckGlobalNames(compiler, options.checkGlobalNamesLevel) .injectNamespace(namespaceForChecks).process(externs, jsRoot); } }; } }; /** Checks that the code is ES5 or Caja compliant. */ final PassFactory checkStrictMode = new PassFactory("checkStrictMode", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new StrictModeCheck(compiler, !options.checkSymbols, // don't check variables twice !options.checkCaja); // disable eval check if not Caja } }; /** Process goog.tweak.getTweak() calls. */ final PassFactory processTweaks = new PassFactory("processTweaks", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { new ProcessTweaks(compiler, options.getTweakProcessing().shouldStrip(), options.getTweakReplacements()).process(externs, jsRoot); } }; } }; /** Override @define-annotated constants. */ final PassFactory processDefines = new PassFactory("processDefines", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { Map<String, Node> replacements = getAdditionalReplacements(options); replacements.putAll(options.getDefineReplacements()); new ProcessDefines(compiler, replacements) .injectNamespace(namespaceForChecks).process(externs, jsRoot); } }; } }; /** Release references to data that is only needed during checks. */ final PassFactory garbageCollectChecks = new HotSwapPassFactory("garbageCollectChecks", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node jsRoot) { // Kill the global namespace so that it can be garbage collected // after all passes are through with it. namespaceForChecks = null; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { process(null, null); } }; } }; /** Checks that all constants are not modified */ final PassFactory checkConsts = new PassFactory("checkConsts", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ConstCheck(compiler); } }; /** Check memory bloat patterns */ final PassFactory check

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>EventfulObjectDisposal = new PassFactory("checkEventfulObjectDisposal", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CheckEventfulObjectDisposal(compiler, options.checkEventfulObjectDisposalPolicy); } }; /** Computes the names of functions for later analysis. */ final PassFactory computeFunctionNames = new PassFactory("computeFunctionNames", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return ((functionNames = new FunctionNames(compiler))); } }; /** Skips Caja-private properties in for-in loops */ final PassFactory ignoreCajaProperties = new PassFactory("ignoreCajaProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new IgnoreCajaProperties(compiler); } }; /** Inserts run-time type assertions for debugging. */ final PassFactory runtimeTypeCheck = new PassFactory("runtimeTypeCheck", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RuntimeTypeCheck(compiler, options.runtimeTypeCheckLogFunction); } }; /** Generates unique ids. */ final PassFactory replaceIdGenerators = new PassFactory("replaceIdGenerators", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { ReplaceIdGenerators pass = new ReplaceIdGenerators( compiler, options.idGenerators, options.generatePseudoNames, options.idGeneratorsMapSerialized); pass.process(externs, root); idGeneratorMap = pass.getSerializedIdMappings(); } }; } }; /** Replace strings. */ final PassFactory replaceStrings = new PassFactory("replaceStrings", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { ReplaceStrings pass = new ReplaceStrings( compiler, options.replaceStringsPlaceholderToken, options.replaceStringsFunctionDescriptions, options.replaceStringsReservedStrings, options.replaceStringsInputMap); pass.process(externs, root); stringMap = pass.getStringMap(); } }; } }; /** Optimizes the "arguments" array. */ final PassFactory optimizeArgumentsArray = new PassFactory("optimizeArgumentsArray", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new OptimizeArgumentsArray(compiler); } }; /** Remove variables set to goog.abstractMethod. */ final PassFactory closureCodeRemoval = new PassFactory("closureCodeRemoval", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ClosureCodeRemoval(compiler, options.removeAbstractMethods, options.removeClosureAsserts); } }; /** Special case optimizations for closure functions. */ final PassFactory closureOptimizePrimitives = new PassFactory("closureOptimizePrimitives", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ClosureOptimizePrimitives(compiler); } }; /** Puts global symbols into a single object. */ final PassFactory rescopeGlobalSymbols =

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> new PassFactory("rescopeGlobalSymbols", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RescopeGlobalSymbols( compiler, options.renamePrefixNamespace, options.renamePrefixNamespaceAssumeCrossModuleNames); } }; /** Collapses names in the global scope. */ final PassFactory collapseProperties = new PassFactory("collapseProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CollapseProperties( compiler, options.collapsePropertiesOnExternTypes, !isInliningForbidden()); } }; /** Rewrite properties as variables. */ final PassFactory collapseObjectLiterals = new PassFactory("collapseObjectLiterals", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineObjectLiterals( compiler, compiler.getUniqueNameIdSupplier()); } }; /** * Try to infer the actual types, which may be narrower * than the declared types. */ final PassFactory tightenTypesBuilder = new PassFactory("tightenTypes", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (!options.checkTypes) { return new ErrorPass(compiler, TIGHTEN_TYPES_WITHOUT_TYPE_CHECK); } tightenTypes = new TightenTypes(compiler); return tightenTypes; } }; /** Disambiguate property names based on the coding convention. */ final PassFactory disambiguatePrivateProperties = new PassFactory("disambiguatePrivateProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new DisambiguatePrivateProperties(compiler); } }; /** Disambiguate property names based on type information. */ final PassFactory disambiguateProperties = new PassFactory("disambiguateProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (tightenTypes == null) { return DisambiguateProperties.forJSTypeSystem(compiler, options.propertyInvalidationErrors); } else { return DisambiguateProperties.forConcreteTypeSystem( compiler, tightenTypes, options.propertyInvalidationErrors); } } }; /** * Chain calls to functions that return this. */ final PassFactory chainCalls = new PassFactory("chainCalls", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ChainCalls(compiler); } }; /** * Rewrite instance methods as static methods, to make them easier * to inline. */ final PassFactory devirtualizePrototypeMethods = new PassFactory("devirtualizePrototypeMethods", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new DevirtualizePrototypeMethods(compiler); } }; /** * Optimizes unused function arguments, unused return values, and inlines * constant parameters. Also runs RemoveUnusedVars. */ final PassFactory optimizeCallsAndRemoveUnusedVars = new PassFactory("optimizeCalls_and_removeUnusedVars", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { OptimizeCalls passes = new OptimizeCalls(compiler); if (

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>options.optimizeReturns) { // Remove unused return values. passes.addPass(new OptimizeReturns(compiler)); } if (options.optimizeParameters) { // Remove all parameters that are constants or unused. passes.addPass(new OptimizeParameters(compiler)); } if (options.optimizeCalls) { boolean removeOnlyLocals = options.removeUnusedLocalVars && !options.removeUnusedVars; boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; passes.addPass( new RemoveUnusedVars(compiler, !removeOnlyLocals, preserveAnonymousFunctionNames, true)); } return passes; } }; /** * Look for function calls that are pure, and annotate them * that way. */ final PassFactory markPureFunctions = new PassFactory("markPureFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new PureFunctionIdentifier.Driver( compiler, options.debugFunctionSideEffectsPath, false); } }; /** * Look for function calls that have no side effects, and annotate them * that way. */ final PassFactory markNoSideEffectCalls = new PassFactory("markNoSideEffectCalls", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new MarkNoSideEffectCalls(compiler); } }; /** Inlines variables heuristically. */ final PassFactory inlineVariables = new PassFactory("inlineVariables", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (isInliningForbidden()) { // In old renaming schemes, inlining a variable can change whether // or not a property is renamed. This is bad, and those old renaming // schemes need to die. return new ErrorPass(compiler, CANNOT_USE_PROTOTYPE_AND_VAR); } else { InlineVariables.Mode mode; if (options.inlineVariables) { mode = InlineVariables.Mode.ALL; } else if (options.inlineLocalVariables) { mode = InlineVariables.Mode.LOCALS_ONLY; } else { throw new IllegalStateException("No variable inlining option set."); } return new InlineVariables(compiler, mode, true); } } }; /** Inlines variables that are marked as constants. */ final PassFactory inlineConstants = new PassFactory("inlineConstants", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineVariables( compiler, InlineVariables.Mode.CONSTANTS_ONLY, true); } }; /** * Perform local control flow optimizations. */ final PassFactory minimizeExitPoints = new PassFactory("minimizeExitPoints", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new MinimizeExitPoints(compiler); } }; /** * Use data flow analysis to remove dead branches. */ final PassFactory removeUnreachableCode = new PassFactory("removeUnreachableCode", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new UnreachableCodeElimination(compiler, true); } }; /** * Remove prototype properties that do not appear to be used. */

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> final PassFactory removeUnusedPrototypeProperties = new PassFactory("removeUnusedPrototypeProperties", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RemoveUnusedPrototypeProperties( compiler, options.removeUnusedPrototypePropertiesInExterns, !options.removeUnusedVars); } }; /** * Remove prototype properties that do not appear to be used. */ final PassFactory removeUnusedClassProperties = new PassFactory("removeUnusedClassProperties", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RemoveUnusedClassProperties(compiler); } }; /** * Process smart name processing - removes unused classes and does referencing * starting with minimum set of names. */ final PassFactory smartNamePass = new PassFactory("smartNamePass", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnalyzer na = new NameAnalyzer(compiler, false); na.process(externs, root); String reportPath = options.reportPath; if (reportPath != null) { try { Files.write(na.getHtmlReport(), new File(reportPath), Charsets.UTF_8); } catch (IOException e) { compiler.report(JSError.make(REPORT_PATH_IO_ERROR, reportPath)); } } if (options.smartNameRemoval) { na.removeUnreferenced(); } } }; } }; /** * Process smart name processing - removes unused classes and does referencing * starting with minimum set of names. */ final PassFactory smartNamePass2 = new PassFactory("smartNamePass", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnalyzer na = new NameAnalyzer(compiler, false); na.process(externs, root); na.removeUnreferenced(); } }; } }; /** Inlines simple methods, like getters */ final PassFactory inlineSimpleMethods = new PassFactory("inlineSimpleMethods", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineSimpleMethods(compiler); } }; /** Kills dead assignments. */ final PassFactory deadAssignmentsElimination = new PassFactory("deadAssignmentsElimination", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new DeadAssignmentsElimination(compiler); } }; /** Inlines function calls. */ final PassFactory inlineFunctions = new PassFactory("inlineFunctions", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { boolean enableBlockInlining = !isInliningForbidden(); return new InlineFunctions( compiler, compiler.getUniqueNameIdSupplier(), options.inlineFunctions, options.inlineLocalFunctions, enableBlockInlining, options.assumeStrictThis() || options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT, options.assumeClosuresOnlyCaptureReferences); } }; /** Inlines constant properties. */ final PassFactory

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> inlineProperties = new PassFactory("inlineProperties", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineProperties(compiler); } }; /** Removes variables that are never used. */ final PassFactory removeUnusedVars = new PassFactory("removeUnusedVars", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { boolean removeOnlyLocals = options.removeUnusedLocalVars && !options.removeUnusedVars; boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; return new RemoveUnusedVars( compiler, !removeOnlyLocals, preserveAnonymousFunctionNames, false); } }; /** * Move global symbols to a deeper common module */ final PassFactory crossModuleCodeMotion = new PassFactory("crossModuleCodeMotion", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph()); } }; /** * Move methods to a deeper common module */ final PassFactory crossModuleMethodMotion = new PassFactory("crossModuleMethodMotion", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CrossModuleMethodMotion( compiler, crossModuleIdGenerator, // Only move properties in externs if we're not treating // them as exports. options.removeUnusedPrototypePropertiesInExterns); } }; /** * Specialize the initial module at the cost of later modules */ final PassFactory specializeInitialModule = new PassFactory("specializeInitialModule", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new SpecializeModule(compiler, devirtualizePrototypeMethods, inlineFunctions, removeUnusedPrototypeProperties); } }; /** A data-flow based variable inliner. */ final PassFactory flowSensitiveInlineVariables = new PassFactory("flowSensitiveInlineVariables", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new FlowSensitiveInlineVariables(compiler); } }; /** Uses register-allocation algorithms to use fewer variables. */ final PassFactory coalesceVariableNames = new PassFactory("coalesceVariableNames", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CoalesceVariableNames(compiler, options.generatePseudoNames); } }; /** * Some simple, local collapses (e.g., {@code var x; var y;} becomes * {@code var x,y;}. */ final PassFactory exploitAssign = new PassFactory("exploitAssign", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new PeepholeOptimizationsPass(compiler, new ExploitAssigns()); } }; /** * Some simple, local collapses (e.g., {@code var x; var y;} becomes * {@code var x,y;}. */ final PassFactory collapseVariableDeclarations = new PassFactory("collapseVariableDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CollapseVariableDeclarations(compiler); } }; /** * Simple global collapses of

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> variable declarations. */ final PassFactory groupVariableDeclarations = new PassFactory("groupVariableDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new GroupVariableDeclarations(compiler); } }; /** * Extracts common sub-expressions. */ final PassFactory extractPrototypeMemberDeclarations = new PassFactory("extractPrototypeMemberDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ExtractPrototypeMemberDeclarations( compiler, Pattern.USE_GLOBAL_TEMP); } }; /** Rewrites common function definitions to be more compact. */ final PassFactory rewriteFunctionExpressions = new PassFactory("rewriteFunctionExpressions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new FunctionRewriter(compiler); } }; /** Collapses functions to not use the VAR keyword. */ final PassFactory collapseAnonymousFunctions = new PassFactory("collapseAnonymousFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CollapseAnonymousFunctions(compiler); } }; /** Moves function declarations to the top, to simulate actual hoisting. */ final PassFactory moveFunctionDeclarations = new PassFactory("moveFunctionDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new MoveFunctionDeclarations(compiler); } }; final PassFactory nameUnmappedAnonymousFunctions = new PassFactory("nameAnonymousFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new NameAnonymousFunctions(compiler); } }; final PassFactory nameMappedAnonymousFunctions = new PassFactory("nameAnonymousFunctions", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnonymousFunctionsMapped naf = new NameAnonymousFunctionsMapped( compiler, options.inputAnonymousFunctionNamingMap); naf.process(externs, root); anonymousFunctionNameMap = naf.getFunctionMap(); } }; } }; /** Alias external symbols. */ final PassFactory aliasExternals = new PassFactory("aliasExternals", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AliasExternals(compiler, compiler.getModuleGraph(), options.unaliasableGlobals, options.aliasableGlobals); } }; /** * Alias string literals with global variables, to avoid creating lots of * transient objects. */ final PassFactory aliasStrings = new PassFactory("aliasStrings", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AliasStrings( compiler, compiler.getModuleGraph(), options.aliasAllStrings ? null : options.aliasableStrings, options.aliasStringsBlacklist, options.outputJsStringUsage); } }; /** Aliases common keywords (true, false) */ final PassFactory aliasKeywords = new PassFactory("aliasKeywords", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AliasKeywords(compiler); } }; /** Handling for the ObjectPropertyString primitive. */ final PassFactory objectPropertyStringPostprocess = new

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> PassFactory("ObjectPropertyStringPostprocess", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ObjectPropertyStringPostprocess(compiler); } }; /** * Renames properties so that the two properties that never appear on * the same object get the same name. */ final PassFactory ambiguateProperties = new PassFactory("ambiguateProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AmbiguateProperties( compiler, options.anonymousFunctionNaming.getReservedCharacters()); } }; /** * Mark the point at which the normalized AST assumptions no longer hold. */ final PassFactory markUnnormalized = new PassFactory("markUnnormalized", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { compiler.setLifeCycleStage(LifeCycleStage.RAW); } }; } }; /** Denormalize the AST for code generation. */ final PassFactory denormalize = new PassFactory("denormalize", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new Denormalize(compiler); } }; /** Inverting name normalization. */ final PassFactory invertContextualRenaming = new PassFactory("invertContextualRenaming", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler); } }; /** * Renames properties. */ final PassFactory renameProperties = new PassFactory("renameProperties", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { final VariableMap prevPropertyMap = options.inputPropertyMap; return new CompilerPass() { @Override public void process(Node externs, Node root) { propertyMap = runPropertyRenaming( compiler, prevPropertyMap, externs, root); } }; } }; private VariableMap runPropertyRenaming( AbstractCompiler compiler, VariableMap prevPropertyMap, Node externs, Node root) { char[] reservedChars = options.anonymousFunctionNaming.getReservedCharacters(); switch (options.propertyRenaming) { case HEURISTIC: RenamePrototypes rproto = new RenamePrototypes(compiler, false, reservedChars, prevPropertyMap); rproto.process(externs, root); return rproto.getPropertyMap(); case AGGRESSIVE_HEURISTIC: RenamePrototypes rproto2 = new RenamePrototypes(compiler, true, reservedChars, prevPropertyMap); rproto2.process(externs, root); return rproto2.getPropertyMap(); case ALL_UNQUOTED: RenameProperties rprop = new RenameProperties( compiler, options.propertyAffinity, options.generatePseudoNames, prevPropertyMap, reservedChars); rprop.process(externs, root); return rprop.getPropertyMap(); default: throw new IllegalStateException( "Unrecognized property renaming policy"); } } /** Renames variables. */ final PassFactory renameVars = new PassFactory("renameVars", true) { @Override protected Compiler

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Pass create(final AbstractCompiler compiler) { final VariableMap prevVariableMap = options.inputVariableMap; return new CompilerPass() { @Override public void process(Node externs, Node root) { variableMap = runVariableRenaming( compiler, prevVariableMap, externs, root); } }; } }; private VariableMap runVariableRenaming( AbstractCompiler compiler, VariableMap prevVariableMap, Node externs, Node root) { char[] reservedChars = options.anonymousFunctionNaming.getReservedCharacters(); boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; Set<String> reservedNames = Sets.newHashSet(); if (options.renamePrefixNamespace != null) { // don't use the prefix name as a global symbol. reservedNames.add(options.renamePrefixNamespace); } if (exportedNames != null) { reservedNames.addAll(exportedNames); } reservedNames.addAll(ParserRunner.getReservedVars()); if (options.alternateRenaming) { RenameVars2 rn = new RenameVars2( compiler, options.renamePrefix, options.variableRenaming == VariableRenamingPolicy.LOCAL, preserveAnonymousFunctionNames, options.generatePseudoNames, options.shadowVariables, prevVariableMap, reservedChars, reservedNames); rn.process(externs, root); return rn.getVariableMap(); } RenameVars rn = new RenameVars( compiler, options.renamePrefix, options.variableRenaming == VariableRenamingPolicy.LOCAL, preserveAnonymousFunctionNames, options.generatePseudoNames, options.shadowVariables, prevVariableMap, reservedChars, reservedNames); rn.process(externs, root); return rn.getVariableMap(); } /** Renames labels */ final PassFactory gatherCharBias = new PassFactory("gatherCharBias", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new GatherCharacterEncodingBias( compiler, getNameGenerator(), options.variableRenaming != VariableRenamingPolicy.LOCAL); } }; /** Renames labels */ final PassFactory renameLabels = new PassFactory("renameLabels", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (options.aggressiveRenaming) { return new RenameLabels(compiler, getNameGenerator()); } else { return new RenameLabels(compiler); } } }; /** Convert bracket access to dot access */ final PassFactory convertToDottedProperties = new PassFactory("convertToDottedProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ConvertToDottedProperties(compiler); } }; /** Checks that all variables are defined. */ final PassFactory sanityCheckAst = new PassFactory("sanityCheckAst", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AstValidator(); } }; /** Checks that all variables are defined. */ final PassFactory sanityCheckVars = new PassFactory("sanityCheckVars", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new VarCheck(compiler, true); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> }; /** Adds instrumentations according to an instrumentation template. */ final PassFactory instrumentFunctions = new PassFactory("instrumentFunctions", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { try { FileReader templateFile = new FileReader(options.instrumentationTemplate); (new InstrumentFunctions( compiler, functionNames, options.instrumentationTemplate, options.appNameStr, templateFile)).process(externs, root); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, options.instrumentationTemplate)); } } }; } }; /** Adds instrumentation for memory allocations. */ final PassFactory instrumentMemoryAllocations = new PassFactory("instrumentMemoryAllocations", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new InstrumentMemoryAllocPass(compiler); } }; final PassFactory instrumentForCodeCoverage = new PassFactory("instrumentForCodeCoverage", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { // TODO(johnlenz): make global instrumentation an option return new CoverageInstrumentationPass( compiler, CoverageReach.CONDITIONAL); } }; /** * Create a no-op pass that can only run once. Used to break up loops. */ static PassFactory createEmptyPass(String name) { return new PassFactory(name, true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return runInSerial(); } }; } /** * Runs custom passes that are designated to run at a particular time. */ private PassFactory getCustomPasses( final CustomPassExecutionTime executionTime) { return new PassFactory("runCustomPasses", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return runInSerial(options.customPasses.get(executionTime)); } }; } /** * All inlining is forbidden in heuristic renaming mode, because inlining * will ruin the invariants that it depends on. */ private boolean isInliningForbidden() { return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || options.propertyRenaming == PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial(final CompilerPass ... passes) { return runInSerial(Lists.newArrayList(passes)); } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial( final Collection<CompilerPass> passes) { return new CompilerPass() { @Override public void process(Node externs, Node root) { for (CompilerPass pass : passes) { pass.process(externs, root); } } }; } @VisibleForTesting static Map<String, Node> getAdditionalReplacements( CompilerOptions options) { Map<String, Node> additionalReplacements = Maps.newHashMap(); if (options.markAsCompiled || options.closurePass) { additionalReplacements

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.put(COMPILED_CONSTANT_NAME, IR.trueNode()); } if (options.closurePass && options.locale != null) { additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME, IR.string(options.locale)); } return additionalReplacements; } final PassFactory printNameReferenceGraph = new PassFactory("printNameReferenceGraph", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); gc.process(externs, jsRoot); String graphFileName = options.nameReferenceGraphPath; try { Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()), new File(graphFileName), Charsets.UTF_8); } catch (IOException e) { compiler.report( JSError.make( NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName)); } } }; } }; final PassFactory printNameReferenceReport = new PassFactory("printNameReferenceReport", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); String reportFileName = options.nameReferenceReportPath; try { NameReferenceGraphReport report = new NameReferenceGraphReport(gc.getNameReferenceGraph()); Files.write(report.getHtmlReport(), new File(reportFileName), Charsets.UTF_8); } catch (IOException e) { compiler.report( JSError.make( NAME_REF_REPORT_FILE_ERROR, e.getMessage(), reportFileName)); } } }; } }; /** * A pass-factory that is good for {@code HotSwapCompilerPass} passes. */ abstract static class HotSwapPassFactory extends PassFactory { HotSwapPassFactory(String name, boolean isOneTimePass) { super(name, isOneTimePass); } @Override protected abstract HotSwapCompilerPass create(AbstractCompiler compiler); @Override HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) { return this.create(compiler); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticScope; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; /** * CodingConvention defines a set of hooks to customize the behavior of the * Compiler for a specific team/company. * */ public interface CodingConvention extends Serializable { /** * This checks whether a given variable name, such as a name in all-caps * should be treated as if it had the @const annotation. * * @param variableName potentially constant variable name * @return {@code true} if the name should be treated as a constant. */ public boolean isConstant(String variableName); /** * This checks whether a given key of an object literal, such as a * name in all-caps should be treated as if it had the @const * annotation. */ public boolean isConstantKey(String keyName); /** * This checks that a given {@code key} may be used as a key for an enum. * * @param key the potential key to an enum * @return {@code true} if the {@code key} may be used as an enum key, * {@code false} otherwise */ public boolean isValidEnumKey(String key); /** * This checks whether a given parameter name should be treated as an * optional parameter as far as type checking or function call arg count * checking is concerned. Note that an optional function parameter may be * declared as a simple type and is automatically converted to a union of the * declared type and Undefined. * * @param parameter The parameter's node. * @return {@code true} if the parameter should be treated as an optional * parameter. */ public boolean isOptionalParameter(Node parameter); /** * This checks whether a given parameter should be treated as a marker * for a variable argument list function. A VarArgs parameter must be the * last parameter in a function declaration. * * @param parameter The parameter's node. * @return {@code true} if the parameter should

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>param alternates the alternates of the union */ UnionType(JSTypeRegistry registry, Collection<JSType> alternates) { super(registry); this.alternates = alternates; this.hashcode = this.alternates.hashCode(); } /** * Gets the alternate types of this union type. * @return The alternate types of this union type. The returned set is * immutable. */ public Collection<JSType> getAlternates() { for (JSType t : alternates) { if (t.isUnionType()) { rebuildAlternates(); break; } } return alternates; } /** * Use UnionTypeBuilder to rebuild the list of alternates and hashcode * of the current UnionType. */ private void rebuildAlternates() { UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (JSType alternate : alternates) { builder.addAlternate(alternate); } alternates = builder.getAlternates(); hashcode = alternates.hashCode(); } /** * This predicate is used to test whether a given type can appear in a * numeric context, such as an operand of a multiply operator. * * @return true if the type can appear in a numeric context. */ @Override public boolean matchesNumberContext() { // TODO(user): Reverse this logic to make it correct instead of generous. for (JSType t : alternates) { if (t.matchesNumberContext()) { return true; } } return false; } /** * This predicate is used to test whether a given type can appear in a * {@code String} context, such as an operand of a string concat ({@code +}) * operator.<p> * * All types have at least the potential for converting to {@code String}. * When we add externally defined types, such as a browser OM, we may choose * to add types that do not automatically convert to {@code String}. * * @return {@code true} if not {@link VoidType} */ @Override public boolean matchesStringContext() { // TODO(user): Reverse this logic to make it correct instead of generous. for (JSType t : alternates) { if (t.matchesStringContext()) { return true; } } return false; } /** * This predicate is used to test whether a given type can appear in an * {@code Object} context, such as the expression in a {@code with} * statement.<p> * * Most types we will encounter, except notably {@code null}, have at least * the potential for converting to {@code Object}. Host defined objects can * get peculiar.<p> * * VOID type is included here because while it is not part of the JavaScript * language, functions returning 'void' type can't be used as operands of * any operator or statement.<p> * * @return {@code true} if the type is not {@link NullType} or * {@link VoidType} */ @Override public boolean matchesObjectContext() { // TODO(user): Reverse this logic to make it correct instead of generous. for (

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>JSType t : alternates) { if (t.matchesObjectContext()) { return true; } } return false; } @Override public JSType findPropertyType(String propertyName) { JSType propertyType = null; for (JSType alternate : getAlternates()) { // Filter out the null/undefined type. if (alternate.isNullType() || alternate.isVoidType()) { continue; } JSType altPropertyType = alternate.findPropertyType(propertyName); if (altPropertyType == null) { continue; } if (propertyType == null) { propertyType = altPropertyType; } else { propertyType = propertyType.getLeastSupertype(altPropertyType); } } return propertyType; } @Override public boolean canBeCalled() { for (JSType t : alternates) { if (!t.canBeCalled()) { return false; } } return true; } @Override public JSType autobox() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternates) { restricted.addAlternate(t.autobox()); } return restricted.build(); } @Override public JSType restrictByNotNullOrUndefined() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternates) { restricted.addAlternate(t.restrictByNotNullOrUndefined()); } return restricted.build(); } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = null; for (JSType t : alternates) { TernaryValue test = t.testForEquality(that); if (result == null) { result = test; } else if (!result.equals(test)) { return UNKNOWN; } } return result; } /** * This predicate determines whether objects of this type can have the * {@code null} value, and therefore can appear in contexts where * {@code null} is expected. * * @return {@code true} for everything but {@code Number} and * {@code Boolean} types. */ @Override public boolean isNullable() { for (JSType t : alternates) { if (t.isNullable()) { return true; } } return false; } @Override public boolean isUnknownType() { for (JSType t : alternates) { if (t.isUnknownType()) { return true; } } return false; } @Override public boolean isStruct() { for (JSType typ : getAlternates()) { if (typ.isStruct()) { return true; } } return false; } @Override public boolean isDict() { for (JSType typ : getAlternates()) { if (typ.isDict()) { return true; } } return false; } @Override public JSType getLeastSupertype(JSType that) { if (!that.isUnknownType() && !that.isUnionType()) { for (JSType alternate : alternates) { if (!alternate.isUnknownType() && that

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.isSubtype(alternate)) { return this; } } } return getLeastSupertype(this, that); } JSType meet(JSType that) { UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (JSType alternate : alternates) { if (alternate.isSubtype(that)) { builder.addAlternate(alternate); } } if (that.isUnionType()) { for (JSType otherAlternate : that.toMaybeUnionType().alternates) { if (otherAlternate.isSubtype(this)) { builder.addAlternate(otherAlternate); } } } else if (that.isSubtype(this)) { builder.addAlternate(that); } JSType result = builder.build(); if (!result.isNoType()) { return result; } else if (this.isObject() && (that.isObject() && !that.isNoType())) { return getNativeType(JSTypeNative.NO_OBJECT_TYPE); } else { return getNativeType(JSTypeNative.NO_TYPE); } } /** * Two union types are equal if, after flattening nested union types, * they have the same number of alternates and all alternates are equal. */ boolean checkUnionEquivalenceHelper( UnionType that, EquivalenceMethod eqMethod) { Collection<JSType> thatAlternates = that.getAlternates(); if (eqMethod == EquivalenceMethod.IDENTITY && getAlternates().size() != thatAlternates.size()) { return false; } for (JSType alternate : thatAlternates) { if (!hasAlternate(alternate, eqMethod)) { return false; } } return true; } private boolean hasAlternate(JSType type, EquivalenceMethod eqMethod) { for (JSType alternate : getAlternates()) { if (alternate.checkEquivalenceHelper(type, eqMethod)) { return true; } } return false; } @Override public boolean hasProperty(String pname) { for (JSType alternate : alternates) { if (alternate.hasProperty(pname)) { return true; } } return false; } @Override public int hashCode() { return this.hashcode; } @Override public UnionType toMaybeUnionType() { return this; } @Override public boolean isObject() { for (JSType alternate : alternates) { if (!alternate.isObject()) { return false; } } return true; } /** * A {@link UnionType} contains a given type (alternate) iff the member * vector contains it. * * @param type The alternate which might be in this union. * * @return {@code true} if the alternate is in the union */ public boolean contains(JSType type) { for (JSType alt : alternates) { if (alt.isEquivalentTo(type)) { return true; } } return false; } /** * Returns a more restricted union type than {@code this} one, in which all * subtypes of {@code type} have been removed.<p> * *

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Examples: * <ul> * <li>{@code (number,string)} restricted by {@code number} is * {@code string}</li> * <li>{@code (null, EvalError, URIError)} restricted by * {@code Error} is {@code null}</li> * </ul> * * @param type the supertype of the types to remove from this union type */ public JSType getRestrictedUnion(JSType type) { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternates) { // Keep all unknown/unresolved types. if (t.isUnknownType() || t.isNoResolvedType() || !t.isSubtype(type)) { restricted.addAlternate(t); } } return restricted.build(); } @Override String toStringHelper(boolean forAnnotations) { StringBuilder result = new StringBuilder(); boolean firstAlternate = true; result.append("("); SortedSet<JSType> sorted = new TreeSet<JSType>(ALPHA); sorted.addAll(alternates); for (JSType t : sorted) { if (!firstAlternate) { result.append("|"); } result.append(t.toStringHelper(forAnnotations)); firstAlternate = false; } result.append(")"); return result.toString(); } @Override public boolean isSubtype(JSType that) { // unknown if (that.isUnknownType()) { return true; } // all type if (that.isAllType()) { return true; } for (JSType element : alternates) { if (!element.isSubtype(that)) { return false; } } return true; } @Override public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { // gather elements after restriction UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { restricted.addAlternate( element.getRestrictedTypeGivenToBooleanOutcome(outcome)); } return restricted.build(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { BooleanLiteralSet literals = BooleanLiteralSet.EMPTY; for (JSType element : alternates) { literals = literals.union(element.getPossibleToBooleanOutcomes()); if (literals == BooleanLiteralSet.BOTH) { break; } } return literals; } @Override public TypePair getTypesUnderEquality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderEquality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public TypePair getTypesUnderInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionType

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Builder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public TypePair getTypesUnderShallowInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderShallowInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseUnionType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseUnionType(this, that); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); // for circularly defined types. // Just resolve the alternates, but do not update as that breaks some error // reporting cases. for (JSType alternate : alternates) { alternate.resolve(t, scope); } // Ensure the union is in a normalized state. rebuildAlternates(); return this; } @Override public String toDebugHashCodeString() { List<String> hashCodes = Lists.newArrayList(); for (JSType a : alternates) { hashCodes.add(a.toDebugHashCodeString()); } return "{(" + Joiner.on(",").join(hashCodes) + ")}"; } @Override public boolean setValidator(Predicate<JSType> validator) { for (JSType a : alternates) { a.setValidator(validator); } return true; } @Override public JSType collapseUnion() { JSType currentValue = null; ObjectType currentCommonSuper = null; for (JSType a : alternates) { if (a.isUnknownType()) { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } ObjectType obj = a.toObjectType(); if (obj == null) { if (currentValue == null && currentCommonSuper == null) { // If obj is not an object, then it must be a value. currentValue = a; } else { // Multiple values and objects will always collapse to the ALL_TYPE. return getNativeType(JSTypeNative.ALL_TYPE); } } else if (currentValue != null) { // Values and objects will always collapse to the ALL_TYPE. return getNativeType(JSTypeNative

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>.ALL_TYPE); } else if (currentCommonSuper == null) { currentCommonSuper = obj; } else { currentCommonSuper = registry.findCommonSuperObject(currentCommonSuper, obj); } } return currentCommonSuper; } @Override public void matchConstraint(JSType constraint) { for (JSType alternate : alternates) { alternate.matchConstraint(constraint); } } @Override public boolean hasAnyTemplateTypesInternal() { for (JSType alternate : alternates) { if (alternate.hasAnyTemplateTypes()) { return true; } } return false; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; /** * The bottom Object type, representing the subclass of all objects. * * Although JavaScript programmers can't explicitly denote the bottom * Object type, it comes up in static analysis. For example, if we have: * <code> * var x = function() {}; * if (x instanceof Array) { * f(x); * } * </code> * We need to be able to assign {@code x} a type within the {@code f(x)} * call. It has no possible type, but {@code x} would not be legal if f * expected a string. So we assign it the {@code NoObjectType}. * * @see <a href="http://en.wikipedia.org/wiki/Bottom_type">Bottom types</a> */ public class NoObjectType extends FunctionType { private static final long serialVersionUID = 1L; NoObjectType(JSTypeRegistry registry) { super(registry, null, null, registry.createArrowType(null, null), null, null, true, true); getInternalArrowType().returnType = this; this.setInstanceType(this); } @Override public boolean isSubtype(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return that.isObject() && !that.isNoType() && !that.isNoResolvedType(); } } @Override public FunctionType toMaybeFunctionType() { return null; } @Override public boolean isNoObjectType() { return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public String getReferenceName() { return null; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public int hashCode() { return System.identityHashCode(this); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing, all properties are defined return true; } @Override public boolean removeProperty(String name) { return false; } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { // Do nothing, specific properties do not have JSDocInfo. } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNoObjectType(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseNoObjectType(that); } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "NoObject"; } @Override public FunctionType getConstructor() { return null; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Find marker's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); token = descriptionInfo.token; } else { token = eatTokensUntilEOL(token); } return token; } private void checkExtendedTypes(List<ExtendedTypeInfo> extendedTypes) { for (ExtendedTypeInfo typeInfo : extendedTypes) { // If interface, record the multiple extended interfaces if (jsdocBuilder.isInterfaceRecorded()) { if (!jsdocBuilder.recordExtendedInterface(typeInfo.type)) { parser.addParserWarning("msg.jsdoc.extends.duplicate", typeInfo.lineno, typeInfo.charno); } } else { if (!jsdocBuilder.recordBaseType(typeInfo.type)) { parser.addTypeWarning("msg.jsdoc.incompat.type", typeInfo.lineno, typeInfo.charno); } } } } /** * Parse a {@code @suppress} tag of the form * {@code @suppress&#123;warning1|warning2&#125;}. * * @param token The current token. */ private JsDocToken parseSuppressTag(JsDocToken token) { if (token == JsDocToken.LC) { Set<String> suppressions = new HashSet<String>(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!suppressionNames.contains(name)) { parser.addParserWarning("msg.jsdoc.suppress.unknown", name, stream.getLineno(), stream.getCharno()); } suppressions.add(stream.getString()); token = next(); } else { parser.addParserWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE, JsDocToken.COMMA)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addParserWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordSuppressions(suppressions)) { parser.addParserWarning("msg.jsdoc.suppress.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Parse a {@code @modifies} tag of the form * {@code @modifies&#123;this|arguments|param&#125;}. * * @param token The current token. */ private JsDocToken parseModifiesTag(JsDocToken token) { if (token == JsDocToken.LC) { Set<String> modifies = new HashSet<String>(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!modifiesAnnotationKeywords.contains(name) && !jsdocBuilder.hasParameter(name)) { parser.addParserWarning("msg.jsdoc.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>modifies.unknown", name, stream.getLineno(), stream.getCharno()); } modifies.add(stream.getString()); token = next(); } else { parser.addParserWarning("msg.jsdoc.modifies", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addParserWarning("msg.jsdoc.modifies", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordModifies(modifies)) { parser.addParserWarning("msg.jsdoc.modifies.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Parse a {@code @idgenerator} tag of the form * {@code @idgenerator} or * {@code @idgenerator&#123;consistent&#125;}. * * @param token The current token. */ private JsDocToken parseIdGeneratorTag(JsDocToken token) { String idgenKind = "unique"; if (token == JsDocToken.LC) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!idGeneratorAnnotationKeywords.contains(name) && !jsdocBuilder.hasParameter(name)) { parser.addParserWarning("msg.jsdoc.idgen.unknown", name, stream.getLineno(), stream.getCharno()); } idgenKind = name; token = next(); } else { parser.addParserWarning("msg.jsdoc.idgen.bad", stream.getLineno(), stream.getCharno()); return token; } if (!match(JsDocToken.RC)) { parser.addParserWarning("msg.jsdoc.idgen.bad", stream.getLineno(), stream.getCharno()); } else { token = next(); } } if (idgenKind.equals("unique")) { if (!jsdocBuilder.recordIdGenerator()) { parser.addParserWarning("msg.jsdoc.idgen.duplicate", stream.getLineno(), stream.getCharno()); } } else if (idgenKind.equals("consistent")) { if (!jsdocBuilder.recordConsistentIdGenerator()) { parser.addParserWarning("msg.jsdoc.idgen.duplicate", stream.getLineno(), stream.getCharno()); } } else if (idgenKind.equals("stable")) { if (!jsdocBuilder.recordStableIdGenerator()) { parser.addParserWarning("msg.jsdoc.idgen.duplicate", stream.getLineno(), stream.getCharno()); } } else if (idgenKind.equals("mapped")) { if (!jsdocBuilder.recordMappedIdGenerator()) { parser.addParserWarning("msg.jsdoc.idgen.duplicate", stream.getLineno(), stream.getCharno()); } } return token

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * A "can cast to" relationship visitor. */ class CanCastToVisitor implements RelationshipVisitor<Boolean> { @Override public Boolean caseUnknownType(JSType thisType, JSType thatType) { return true; } @Override public Boolean caseNoType(JSType thatType) { return true; } @Override public Boolean caseNoObjectType(JSType thatType) { return true; // TODO(johnlenz): restrict to objects } @Override public Boolean caseAllType(JSType thatType) { return true; } boolean canCastToUnion(JSType thisType, UnionType unionType) { for (JSType type : unionType.getAlternates()) { if (thisType.visit(this, type)) { return true; } } return false; } boolean canCastToFunction(JSType thisType, FunctionType functionType) { if (thisType.isFunctionType()) { // TODO(johnlenz): visit function parts return true; } else { return thisType.isSubtype(functionType) || functionType.isSubtype(thisType); } } private boolean isInterface(JSType type) { ObjectType objType = type.toObjectType(); if (objType != null) { JSType constructor = objType.getConstructor(); return constructor != null

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> && constructor.isInterface(); } return false; } Boolean castCastToHelper(JSType thisType, JSType thatType) { if (thatType.isUnknownType() || thatType.isAllType() || thatType.isNoObjectType() // TODO(johnlenz): restrict to objects || thatType.isNoType()) { return true; } else if (thisType.isRecordType() || thatType.isRecordType()) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (isInterface(thisType) || isInterface(thatType)) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (thatType.isEnumElementType()) { return thisType.visit(this, thatType.toMaybeEnumElementType().getPrimitiveType()); } else if (thatType.isUnionType()) { return canCastToUnion(thisType, thatType.toMaybeUnionType()); } else if (thatType.isFunctionType()) { return canCastToFunction(thisType, thatType.toMaybeFunctionType()); } else if (thatType.isTemplatizedType()) { // TODO(johnlenz): once the templated type work is finished, // restrict the type parameters. return thisType.visit(this, thatType.toMaybeTemplatizedType().getReferencedTypeInternal()); } return thisType.isSubtype(thatType) || thatType.isSubtype(thisType); } @Override public Boolean caseValueType(ValueType thisType, JSType thatType) { return castCastToHelper(thisType, thatType); } @Override public Boolean caseObjectType(ObjectType thisType, JSType thatType) { return castCastToHelper(thisType, thatType); } @Override public Boolean caseFunctionType(FunctionType thisType, JSType thatType) { return castCastToHelper(thisType, thatType); } @Override public Boolean caseUnionType(UnionType thisType, JSType thatType) { boolean visited = false; for (JSType type : thisType.getAlternates()) { if (type.isVoidType() || type.isNullType()) { // Don't allow if the only match between the types is null or void, // otherwise any nullable type would be castable to any other nullable // type and we don't want that. } else { visited = true; if (type.visit(this, thatType)) { return true; } } } // Special case the "null|undefined" union and allow it to be cast // to any cast to any type containing allowing either null|undefined. if (!visited) { JSType NULL_TYPE = thisType.getNativeType(JSTypeNative.NULL_TYPE); JSType VOID_TYPE = thisType.getNativeType(JSTypeNative.VOID_TYPE); return NULL_TYPE.visit(this, thatType) || VOID_TYPE.visit(this, thatType); } return false; } @Override public Boolean caseTemplatizedType( TemplatizedType thisType, JSType thatType) { // TODO(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>johnlenz): once the templated type work is finished, // restrict the type parameters. return thisType.getReferencedTypeInternal().visit(this, thatType); } @Override public Boolean caseTemplateType(TemplateType thisType, JSType thatType) { return true; } @Override public Boolean caseEnumElementType( EnumElementType typeType, JSType thatType) { return typeType.getPrimitiveType().visit(this, thatType); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> 1L; public GraphAnnotationState(int size) { super(size); } } /** * Used by {@link #pushNodeAnnotations()} and {@link #popNodeAnnotations()}. */ private Deque<GraphAnnotationState> nodeAnnotationStack; /** * Used by {@link #pushEdgeAnnotations()} and {@link #popEdgeAnnotations()}. */ private Deque<GraphAnnotationState> edgeAnnotationStack; /** * Connects two nodes in the graph with an edge. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public abstract void connect(N n1, E edge, N n2); /** * Disconnects two nodes in the graph by removing all edges between them. * * @param n1 First node. * @param n2 Second node. */ public abstract void disconnect(N n1, N n2); /** * Connects two nodes in the graph with an edge if such edge does not already * exists between the nodes. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public final void connectIfNotFound(N n1, E edge, N n2) { if (!isConnected(n1, edge, n2)) { connect(n1, edge, n2); } } /** * Gets a node from the graph given a value. New nodes are created if that * value has not been assigned a graph node. Values equality are compared * using <code>Object.equals</code>. * * @param value The node's value. * @return The corresponding node in the graph. */ public abstract GraphNode<N, E> createNode(N value); /** Gets an immutable list of all nodes. */ @Override public abstract Collection<? extends GraphNode<N, E>> getNodes(); /** Gets an immutable list of all edges. */ public abstract List<? extends GraphEdge<N, E>> getEdges(); /** * Gets the degree of a node. * * @param value The node's value. * @return The degree of the node. */ public abstract int getNodeDegree(N value); @Override public int getWeight(N value) { return getNodeDegree(value); } /** * Gets the neighboring nodes. * * @param value The node's value. * @return A list of neighboring nodes. */ public abstract List<GraphNode<N, E>> getNeighborNodes(N value); public abstract Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value); /** * Retrieves an edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The list of edges between those two values in the graph. */ public abstract List<? extends GraphEdge<N, E>> getEdges(N n1, N n2); /** * Retrieves any edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The first edges between those two values in the graph. null if

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * there are none. */ public abstract GraphEdge<N, E> getFirstEdge(N n1, N n2); /** * Checks whether the node exists in the graph ({@link #createNode(Object)} * has been called with that value). * * @param n Node. * @return <code>true</code> if it exist. */ public final boolean hasNode(N n) { return getNode(n) != null; } /** * Checks whether two nodes in the graph are connected. * * @param n1 Node 1. * @param n2 Node 2. * @return <code>true</code> if the two nodes are connected. */ public abstract boolean isConnected(N n1, N n2); /** * Checks whether two nodes in the graph are connected by the given * edge type. * * @param n1 Node 1. * @param e The edge type. * @param n2 Node 2. */ public abstract boolean isConnected(N n1, E e, N n2); /** * Gets the node of the specified type, or throws an * IllegalArgumentException. */ @SuppressWarnings("unchecked") <T extends GraphNode<N, E>> T getNodeOrFail(N val) { T node = (T) getNode(val); if (node == null) { throw new IllegalArgumentException(val + " does not exist in graph"); } return node; } @Override public final void clearNodeAnnotations() { for (GraphNode<N, E> n : getNodes()) { n.setAnnotation(null); } } /** Makes each edge's annotation null. */ public final void clearEdgeAnnotations() { for (GraphEdge<N, E> e : getEdges()) { e.setAnnotation(null); } } /** * Pushes nodes' annotation values. Restored with * {@link #popNodeAnnotations()}. Nodes' annotation values are cleared. */ public final void pushNodeAnnotations() { if (nodeAnnotationStack == null) { nodeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(nodeAnnotationStack, getNodes()); } /** * Restores nodes' annotation values to state before last * {@link #pushNodeAnnotations()}. */ public final void popNodeAnnotations() { Preconditions.checkNotNull(nodeAnnotationStack, "Popping node annotations without pushing."); popAnnotations(nodeAnnotationStack); } /** * Pushes edges' annotation values. Restored with * {@link #popEdgeAnnotations()}. Edges' annotation values are cleared. */ public final void pushEdgeAnnotations() { if (edgeAnnotationStack == null) { edgeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(edgeAnnotationStack, getEdges()); } /** * Restores edges' annotation values to state before last * {@link #pushEdgeAnnotations()}. */ public final void popEdgeAnnotations() { Preconditions.checkNotNull(edgeAnnotationStack, "Popping edge annotations without pushing."); popAnnotations(edgeAnnotationStack); } /** * A generic edge. * * @param <N> Value type that the graph node stores. *

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> @param <E> Value type that the graph edge stores. */ public interface GraphEdge<N, E> extends Annotatable { /** * Retrieves the edge's value. * * @return The value. */ E getValue(); GraphNode<N, E> getNodeA(); GraphNode<N, E> getNodeB(); } /** * A simple implementation of SubGraph that calculates adjacency by iterating * over a node's neighbors. */ class SimpleSubGraph<N, E> implements SubGraph<N, E> { private Graph<N, E> graph; private List<GraphNode<N, E>> nodes = Lists.newArrayList(); SimpleSubGraph(Graph<N, E> graph) { this.graph = graph; } @Override public boolean isIndependentOf(N value) { GraphNode<N, E> node = graph.getNode(value); for (GraphNode<N, E> n : nodes) { if (graph.getNeighborNodes(n.getValue()).contains(node)) { return false; } } return true; } @Override public void addNode(N value) { nodes.add(graph.getNodeOrFail(value)); } } /** * Pushes a new list on stack and stores nodes annotations in the new list. * Clears objects' annotations as well. */ private static void pushAnnotations( Deque<GraphAnnotationState> stack, Collection<? extends Annotatable> haveAnnotations) { stack.push(new GraphAnnotationState(haveAnnotations.size())); for (Annotatable h : haveAnnotations) { stack.peek().add(new AnnotationState(h, h.getAnnotation())); h.setAnnotation(null); } } /** * Restores the node annotations on the top of stack and pops stack. */ private static void popAnnotations(Deque<GraphAnnotationState> stack) { for (AnnotationState as : stack.pop()) { as.first.setAnnotation(as.second); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Predicate; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.GraphReachability; import com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.TernaryValue; /** * Use {@link ControlFlowGraph} and {@link GraphReachability} to inform user * about unreachable code. * */ class CheckUnreachableCode implements ScopedCallback { static final DiagnosticType UNREACHABLE_CODE = DiagnosticType.error( "JSC_UNREACHABLE_CODE", "unreachable code"); private final AbstractCompiler compiler; private final CheckLevel level; CheckUnreachableCode(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.level = level; } @Override public void enterScope(NodeTraversal t) { initScope(t.getControlFlowGraph()); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { GraphNode<Node, Branch> gNode = t.getControlFlowGraph().getNode(n); if (gNode != null && gNode.getAnnotation() != GraphReachability.REACHABLE) { // Only report error when there are some line number informations. // There are synthetic nodes with no line number informations, nodes // introduce by other passes (although not likely since this pass should // be executed early) or some rhino bug. if (n.getLineno() != -1 && // Allow spurious semi-colons and spurious breaks. !n.isEmpty() && !n.isBreak()) { compiler.report(t.makeError(n, level, UNREACHABLE_CODE)); // From now on, we are going to assume the user fixed the error and not // give more warning related to code section reachable from this node. new GraphReachability<Node, ControlFlowGraph.Branch>( t.getControlFlowGraph()).recompute(n); // Saves time by not traversing children. return false; } } return true; } private void initScope(ControlFlowGraph<Node> controlFlowGraph) { new GraphReachability<Node,

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> ControlFlowGraph.Branch>( controlFlowGraph, new ReachablePredicate()).compute( controlFlowGraph.getEntry().getValue()); } @Override public void exitScope(NodeTraversal t) { } @Override public void visit(NodeTraversal t, Node n, Node parent) { } private final class ReachablePredicate implements Predicate<EdgeTuple<Node, ControlFlowGraph.Branch>> { @Override public boolean apply(EdgeTuple<Node, Branch> input) { Branch branch = input.edge; if (!branch.isConditional()) { return true; } Node predecessor = input.sourceNode; Node condition = NodeUtil.getConditionExpression(predecessor); // TODO(user): Handle more complicated expression like true == true, // etc.... if (condition != null) { TernaryValue val = NodeUtil.getImpureBooleanValue(condition); if (val != TernaryValue.UNKNOWN) { return val.toBoolean(true) == (branch == Branch.ON_TRUE); } } return true; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.ImmutableSet; import java.util.Set; /** * Abstract message formatter providing default behavior for implementations * of {@link MessageFormatter} needing a {@link SourceExcerptProvider}. * */ public abstract class AbstractMessageFormatter implements MessageFormatter { private final SourceExcerptProvider source; private boolean colorize; public AbstractMessageFormatter(SourceExcerptProvider source) { this.source = source; } public void setColorize(boolean colorize) { this.colorize = colorize; } /** * Get the source excerpt provider. */ protected final SourceExcerptProvider getSource() { return source; } private static final Set<String> SUPPORTED_COLOR_TERMINALS = ImmutableSet.of("xterm", "xterm-color", "xterm-256color", "screen-bce"); static boolean termSupportsColor(String term) { return SUPPORTED_COLOR_TERMINALS.contains(term); } private static enum Color { ERROR("\033[31m"), WARNING("\033[35m"), RESET("\033[39m"); private final String controlCharacter; Color(String controlCharacter) { this.controlCharacter = controlCharacter; } public String getControlCharacter() { return controlCharacter; } } String getLevelName(CheckLevel level) { switch (level) { case ERROR: return maybeColorize("ERROR", Color.ERROR); case WARNING: return maybeColorize("WARNING", Color.WARNING); default: return level.toString(); } } private String maybeColorize(String text, Color color) { if (!colorize) { return text; } return color.getControlCharacter() + text + Color.RESET.getControlCharacter(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2012 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Checks for common errors, such as misplaced semicolons: * <pre> * if (x); act_now(); * </pre> * or comparison against NaN: * <pre> * if (x === NaN) act(); * </pre> * and generates warnings. * * @author johnlenz@google.com (John Lenz) */ final class CheckSuspiciousCode extends AbstractPostOrderCallback { static final DiagnosticType SUSPICIOUS_SEMICOLON = DiagnosticType.warning( "JSC_SUSPICIOUS_SEMICOLON", "If this if/for/while really shouldn't have a body, use {}"); static final DiagnosticType SUSPICIOUS_COMPARISON_WITH_NAN = DiagnosticType.warning( "JSC_SUSPICIOUS_NAN", "Comparison again NaN is always false. Did you mean isNaN()?"); CheckSuspiciousCode() { } @Override public void visit(NodeTraversal t, Node n, Node parent) { checkMissingSemicolon(t, n); checkNaN(t, n); } private void checkMissingSemicolon(NodeTraversal t, Node n) { switch (n.getType()) { case Token.IF: Node trueCase = n.getFirstChild().getNext(); reportIfWasEmpty(t, trueCase); Node elseCase = trueCase.getNext(); if (elseCase != null) { reportIfWasEmpty(t, elseCase); } break; case Token.WHILE: case Token.FOR: reportIfWasEmpty(t, NodeUtil.getLoopCodeBlock(n)); break; } } private void reportIfWasEmpty(NodeTraversal t, Node block) { Preconditions.checkState(block.isBlock()); // A semicolon is distinguished from a block without children by // annotating it with EMPTY_BLOCK. Blocks without children are // usually intentional, especially with loops. if (!block.hasChildren() && block.wasEmptyNode()) { t.getCompiler().report( t.makeError(block, SUSPICIOUS_SEMICOLON)); } } private void checkNaN(NodeTraversal t, Node n) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Ast(file), isExtern); } /** Returns a name for this input. Must be unique across all inputs. */ @Override public InputId getInputId() { return id; } /** Returns a name for this input. Must be unique across all inputs. */ @Override public String getName() { return id.getIdName(); } public SourceAst getAst() { return ast; } /** Gets the path relative to closure-base, if one is available. */ @Override public String getPathRelativeToClosureBase() { // TODO(nicksantos): Implement me. throw new UnsupportedOperationException(); } @Override public Node getAstRoot(AbstractCompiler compiler) { Node root = ast.getAstRoot(compiler); // The root maybe null if the AST can not be created. if (root != null) { Preconditions.checkState(root.isScript()); Preconditions.checkNotNull(root.getInputId()); } return root; } @Override public void clearAst() { ast.clearAst(); } @Override public SourceFile getSourceFile() { return ast.getSourceFile(); } @Override public void setSourceFile(SourceFile file) { ast.setSourceFile(file); } /** Returns the SourceAst object on which this input is based. */ public SourceAst getSourceAst() { return ast; } /** Sets an abstract compiler for doing parsing. */ public void setCompiler(AbstractCompiler compiler) { this.compiler = compiler; } private void checkErrorManager() { Preconditions.checkNotNull(compiler, "Expected setCompiler to be called first: " + this); Preconditions.checkNotNull(compiler.getErrorManager(), "Expected compiler to call an error manager: " + this); } /** Gets a list of types depended on by this input. */ @Override public Collection<String> getRequires() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return Collections.<String>unmodifiableSet(requires); } catch (IOException e) { compiler.getErrorManager().report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.<String>of(); } } /** Gets a list of types provided by this input. */ @Override public Collection<String> getProvides() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return Collections.<String>unmodifiableSet(provides); } catch (IOException e) { compiler.getErrorManager().report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.<String>of(); } } // TODO(nicksantos): Remove addProvide/addRequire/removeRequire once // there is better support for discovering non-closure dependencies. void addProvide(String provide) { getProvides(); provides.add(provide); } void addRequire(String require) { getRequires(); requires.add(require); } public void removeRequire(String require) { getRequires(); requires.remove(require); } /** * Regenerates the provides/requires if we need to do so. */ private void re

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> getSourceFile().getLine(lineNumber); } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public Region getRegion(int lineNumber) { return getSourceFile().getRegion(lineNumber); } public String getCode() throws IOException { return getSourceFile().getCode(); } /** Returns the module to which the input belongs. */ public JSModule getModule() { return module; } /** Sets the module to which the input belongs. */ public void setModule(JSModule module) { // An input may only belong to one module. Preconditions.checkArgument( module == null || this.module == null || this.module == module); this.module = module; } /** Overrides the module to which the input belongs. */ void overrideModule(JSModule module) { this.module = module; } public boolean isExtern() { if (ast == null || ast.getSourceFile() == null) { return false; } return ast.getSourceFile().isExtern(); } void setIsExtern(boolean isExtern) { if (ast == null || ast.getSourceFile() == null) { return; } ast.getSourceFile().setIsExtern(isExtern); } public int getLineOffset(int lineno) { return ast.getSourceFile().getLineOffset(lineno); } /** @return The number of lines in this input. */ public int getNumLines() { return ast.getSourceFile().getNumLines(); } @Override public String toString() { return getName(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Warning(this); default: return null; } } @Override public String toString() { // TODO(user): remove custom toString. return type.key + ". " + description + " at " + (sourceName != null && sourceName.length() > 0 ? sourceName : "(unknown source)") + " line " + (lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)") + " : " + (charno != -1 ? String.valueOf(charno) : "(unknown column)"); } /** * Get the character number. */ public int getCharno() { return charno; } /** * Get the line number. One-based. */ public int getLineNumber() { return lineNumber; } /** * @return the offset of the region the Error applies to, or -1 if the offset * is unknown. */ public int getNodeSourceOffset() { return node != null ? node.getSourceOffset() : -1; } /** * @return the length of the region the Error applies to, or 0 if the length * is unknown. */ public int getNodeLength() { return node != null ? node.getLength() : 0; } /** The default level, before any of the WarningsGuards are applied. */ public CheckLevel getDefaultLevel() { return defaultLevel; } @Override public boolean equals(Object o) { // Generated by Intellij IDEA if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JSError jsError = (JSError) o; if (charno != jsError.charno) { return false; } if (lineNumber != jsError.lineNumber) { return false; } if (!description.equals(jsError.description)) { return false; } if (defaultLevel != jsError.defaultLevel) { return false; } if (sourceName != null ? !sourceName.equals(jsError.sourceName) : jsError.sourceName != null) { return false; } if (!type.equals(jsError.type)) { return false; } return true; } @Override public int hashCode() { // Generated by Intellij IDEA int result = type.hashCode(); result = 31 * result + description.hashCode(); result = 31 * result + (sourceName != null ? sourceName.hashCode() : 0); result = 31 * result + lineNumber; result = 31 * result + defaultLevel.hashCode(); result = 31 * result + charno; return result; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Type == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); Preconditions.checkNotNull(qualifiedName); JSType origType = node.getJSType(); origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; scope.inferQualifiedSlot(node, qualifiedName, origType, type); break; case Token.THIS: // "this" references aren't currently modeled in the CFG. break; default: throw new IllegalArgumentException("Node cannot be refined. \n" + node.toStringTree()); } } /** * @see #getRestrictedWithoutUndefined(JSType) */ private final Visitor<JSType> restrictUndefinedVisitor = new Visitor<JSType>() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return getNativeType(NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE); } @Override public JSType caseUnionType(UnionType type) { return type.getRestrictedUnion(getNativeType(VOID_TYPE)); } @Override public JSType caseUnknownType() { return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseVoidType() { return null; } @Override public JSType caseTemplatizedType(TemplatizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>TemplateType templateType) { return caseObjectType(templateType); } }; /** * @see #getRestrictedWithoutNull(JSType) */ private final Visitor<JSType> restrictNullVisitor = new Visitor<JSType>() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, VOID_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return null; } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE); } @Override public JSType caseUnionType(UnionType type) { return type.getRestrictedUnion(getNativeType(NULL_TYPE)); } @Override public JSType caseUnknownType() { return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseVoidType() { return getNativeType(VOID_TYPE); } @Override public JSType caseTemplatizedType(TemplatizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(TemplateType templateType) { return caseObjectType(templateType); } }; /** * A class common to all visitors that need to restrict the type based on * {@code typeof}-like conditions. */ abstract class RestrictByTypeOfResultVisitor implements Visitor<JSType> { /** * Abstracts away the similarities between visiting the unknown type and the * all type. * @param topType {@code UNKNOWN_TYPE} or {@code ALL_TYPE} * @return the restricted type * @see #caseAllType * @see #caseUnknownType */ protected abstract JSType caseTopType(JSType topType); @Override public JSType caseAllType() { return caseTopType(getNativeType(ALL_TYPE)); } @Override public JSType caseUnknownType() { return caseTopType(getNativeType(CHECKED_UNKNOWN_TYPE)); } @Override public JSType caseUnionType(UnionType type) { JSType restricted = null; for (JSType alternate : type.getAlternates()) { JSType restrictedAlternate = alternate.visit(this); if

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> (restrictedAlternate != null) { if (restricted == null) { restricted = restrictedAlternate; } else { restricted = restrictedAlternate.getLeastSupertype(restricted); } } } return restricted; } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseEnumElementType(EnumElementType enumElementType) { // NOTE(nicksantos): This is a white lie. Suppose we have: // /** @enum {string|number} */ var MyEnum = ...; // if (goog.isNumber(myEnumInstance)) { // /* what is myEnumInstance here? */ // } // There is no type that represents {MyEnum - string}. What we really // need is a notion of "enum subtyping", so that we could dynamically // create a subtype of MyEnum restricted by string. In any case, // this should catch the common case. JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { return enumElementType; } else { return type; } } @Override public JSType caseTemplatizedType(TemplatizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(TemplateType templateType) { return caseObjectType(templateType); } } /** * A class common to all visitors that need to restrict the type based on * some {@code typeof}-like condition being true. All base cases return * {@code null}. It is up to the subclasses to override the appropriate ones. */ abstract class RestrictByTrueTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { @Override public JSType caseNoObjectType() { return null; } @Override public JSType caseBooleanType() { return null; } @Override public JSType caseFunctionType(FunctionType type) { return null; } @Override public JSType caseNullType() { return null; } @Override public JSType caseNumberType() { return null; } @Override public JSType caseObjectType(ObjectType type) { return null; } @Override public JSType caseStringType() { return null; } @Override public JSType caseVoidType() { return null; } } /** * A class common to all visitors that need to restrict the type based on * some {@code typeof}-like condition being false. All base cases return * their type. It is up to the subclasses to override the appropriate ones. */ abstract class RestrictByFalseTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { @Override protected JSType caseTopType(JSType topType) { return topType; } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNull

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Type() { return getNativeType(NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE); } @Override public JSType caseVoidType() { return getNativeType(VOID_TYPE); } } /** * @see ChainableReverseAbstractInterpreter#getRestrictedByTypeOfResult */ private class RestrictByOneTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { /** * A value known to be equal or not equal to the result of the * {@code typeOf} operation. */ private final String value; /** * {@code true} if the {@code typeOf} result is known to equal * {@code value}; {@code false} if it is known <em>not</em> to equal * {@code value}. */ private final boolean resultEqualsValue; RestrictByOneTypeOfResultVisitor(String value, boolean resultEqualsValue) { this.value = value; this.resultEqualsValue = resultEqualsValue; } /** * Computes whether the given result of a {@code typeof} operator matches * expectations, i.e. whether a type that gives such a result should be * kept. */ private boolean matchesExpectation(String result) { return result.equals(value) == resultEqualsValue; } @Override protected JSType caseTopType(JSType topType) { JSType result = topType; if (resultEqualsValue) { JSType typeByName = getNativeTypeForTypeOf(value); if (typeByName != null) { result = typeByName; } } return result; } @Override public JSType caseNoObjectType() { return (value.equals("object") || value.equals("function")) == resultEqualsValue ? getNativeType(NO_OBJECT_TYPE) : null; } @Override public JSType caseBooleanType() { return matchesExpectation("boolean") ? getNativeType(BOOLEAN_TYPE) : null; } @Override public JSType caseFunctionType(FunctionType type) { return matchesExpectation("function") ? type : null; } @Override public JSType caseNullType() { return matchesExpectation("object") ? getNativeType(NULL_TYPE) : null; } @Override public JSType caseNumberType() { return matchesExpectation("number") ? getNativeType(NUMBER_TYPE) : null; } @Override public JSType caseObjectType(ObjectType type) { if (value.equals("function")) { JSType ctorType = getNativeType(U2U_CONSTRUCTOR_TYPE); if (resultEqualsValue) { // Objects are restricted to "Function", subtypes are left return ctorType.getGreatestSubtype(type); } else { // Only filter out subtypes of "function" return type.isSubtype(ctorType) ? null : type; } } return matchesExpectation("object") ? type : null; } @Override public JSType caseStringType() { return matches

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Expectation("string") ? getNativeType(STRING_TYPE) : null; } @Override public JSType caseVoidType() { return matchesExpectation("undefined") ? getNativeType(VOID_TYPE) : null; } } /** * Returns a version of type where undefined is not present. */ protected final JSType getRestrictedWithoutUndefined(JSType type) { return type == null ? null : type.visit(restrictUndefinedVisitor); } /** * Returns a version of type where null is not present. */ protected final JSType getRestrictedWithoutNull(JSType type) { return type == null ? null : type.visit(restrictNullVisitor); } /** * Returns a version of {@code type} that is restricted by some knowledge * about the result of the {@code typeof} operation. * <p> * The behavior of the {@code typeof} operator can be summarized by the * following table: * <table> * <tr><th>type</th><th>result</th></tr> * <tr><td>{@code undefined}</td><td>"undefined"</td></tr> * <tr><td>{@code null}</td><td>"object"</td></tr> * <tr><td>{@code boolean}</td><td>"boolean"</td></tr> * <tr><td>{@code number}</td><td>"number"</td></tr> * <tr><td>{@code string}</td><td>"string"</td></tr> * <tr><td>{@code Object} (which doesn't implement [[Call]])</td> * <td>"object"</td></tr> * <tr><td>{@code Object} (which implements [[Call]])</td> * <td>"function"</td></tr> * </table> * @param type the type to restrict * @param value A value known to be equal or not equal to the result of the * {@code typeof} operation * @param resultEqualsValue {@code true} if the {@code typeOf} result is known * to equal {@code value}; {@code false} if it is known <em>not</em> to * equal {@code value} * @return the restricted type or null if no version of the type matches the * restriction */ JSType getRestrictedByTypeOfResult(JSType type, String value, boolean resultEqualsValue) { if (type == null) { if (resultEqualsValue) { JSType result = getNativeTypeForTypeOf(value); return result == null ? getNativeType(CHECKED_UNKNOWN_TYPE) : result; } else { return null; } } return type.visit( new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue)); } JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } /** * If we definitely know what a type is based on the typeof result, * return it. Otherwise, return null. * * The typeof operation in JS is poorly defined, and this function works * for both the native typeof and goog.typeOf. It should not be made public, * because its semantics are informally defined

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Maps; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Filters warnings based on in-code {@code @suppress} annotations. * @author nicksantos@google.com (Nick Santos) */ class SuppressDocWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; /** Warnings guards for each suppressible warnings group, indexed by name. */ private final Map<String, DiagnosticGroupWarningsGuard> suppressors = Maps.newHashMap(); /** * The suppressible groups, indexed by name. */ SuppressDocWarningsGuard(Map<String, DiagnosticGroup> suppressibleGroups) { for (Map.Entry<String, DiagnosticGroup> entry : suppressibleGroups.entrySet()) { suppressors.put( entry.getKey(), new DiagnosticGroupWarningsGuard( entry.getValue(), CheckLevel.OFF)); } } @Override public CheckLevel level(JSError error) { Node node = error.node; if (node != null) { boolean visitedFunction = false; for (Node current = node; current != null; current = current.getParent()) { int type = current.getType(); JSDocInfo info = null; if (type == Token.FUNCTION) { info = NodeUtil.getBestJSDocInfo(current); visitedFunction = true; } else if (type == Token.SCRIPT) { info = current.getJSDocInfo(); } else if (current.isVar() || current.isAssign()) { // There's one edge case we're worried about: // if the warning points to an assigment to a function, we // want the suppressions on that function to apply. // It's OK if we double-count some cases. Node rhs = NodeUtil.getRValueOfLValue(current.getFirstChild()); if (rhs != null) { if (rhs.isCast()) { rhs = rhs.getFirstChild(); } if (rhs.isFunction() && !visitedFunction) { info = NodeUtil.getBestJSDocInfo(current); } } } if (info != null) { for (String suppressor : info.getSuppressions()) { WarningsGuard guard = suppressors.get(suppressor); // Some @suppress

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> tags are for other tools, and // may not have a warnings guard. if (guard != null) { CheckLevel newLevel = guard.level(error); if (newLevel != null) { return newLevel; } } } } } } return null; } @Override public int getPriority() { // Happens after path-based filtering, but before other times // of filtering. return WarningsGuard.Priority.SUPPRESS_DOC.value; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>NaN; } if (name.equals("Infinity")) { return Double.POSITIVE_INFINITY; } return null; case Token.NEG: if (n.getChildCount() == 1 && n.getFirstChild().isName() && n.getFirstChild().getString().equals("Infinity")) { return Double.NEGATIVE_INFINITY; } return null; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? 0.0 : 1.0; // reversed. } break; case Token.STRING: return getStringNumberValue(n.getString()); case Token.ARRAYLIT: case Token.OBJECTLIT: String value = getStringValue(n); return value != null ? getStringNumberValue(value) : null; } return null; } static Double getStringNumberValue(String rawJsString) { if (rawJsString.contains("\u000b")) { // vertical tab is not always whitespace return null; } String s = trimJsWhiteSpace(rawJsString); // return ScriptRuntime.toNumber(s); if (s.length() == 0) { return 0.0; } if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { // Attempt to convert hex numbers. try { return Double.valueOf(Integer.parseInt(s.substring(2), 16)); } catch (NumberFormatException e) { return Double.NaN; } } if (s.length() > 3 && (s.charAt(0) == '-' || s.charAt(0) == '+') && s.charAt(1) == '0' && (s.charAt(2) == 'x' || s.charAt(2) == 'X')) { // hex numbers with explicit signs vary between browsers. return null; } // Firefox and IE treat the "Infinity" differently. Firefox is case // insensitive, but IE treats "infinity" as NaN. So leave it alone. if (s.equals("infinity") || s.equals("-infinity") || s.equals("+infinity")) { return null; } try { return Double.parseDouble(s); } catch (NumberFormatException e) { return Double.NaN; } } static String trimJsWhiteSpace(String s) { int start = 0; int end = s.length(); while (end > 0 && isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) { end--; } while (start < end && isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) { start++; } return s.substring(start, end); } /** * Copied from Rhino's ScriptRuntime */ public static TernaryValue isStrWhiteSpaceChar(int c) { switch (c) { case '\u000B': // <VT>

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> any captured variables directly, it would still fail this definition, * because it affects the lifecycle of variables in the enclosing scope. * * However, a function literal with respect to a particular scope is * a literal. * * @param includeFunctions If true, all function expressions will be * treated as literals. */ static boolean isLiteralValue(Node n, boolean includeFunctions) { switch (n.getType()) { case Token.CAST: return isLiteralValue(n.getFirstChild(), includeFunctions); case Token.ARRAYLIT: for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if ((!child.isEmpty()) && !isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.OBJECTLIT: // Return true only if all values are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { return false; } } return true; case Token.FUNCTION: return includeFunctions && !NodeUtil.isFunctionDeclaration(n); default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Binary operators are only valid if both children are valid. case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return isValidDefineValue(val.getFirstChild(), defines) && isValidDefineValue(val.getLastChild(), defines); // Unary operators are valid if the child is valid. case Token.NOT: case Token.NEG: case Token.POS: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. *

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Target.getFirstChild(); if (evaluatesToLocalValue(current)) { return false; } // A literal value as defined by "isLiteralValue" is guaranteed // not to be an alias, or any components which are aliases of // other objects. // If the root object is a literal don't consider this a // side-effect. while (isGet(current)) { current = current.getFirstChild(); } return !isLiteralValue(current, true); } else { // TODO(johnlenz): remove this code and make this an exception. This // is here only for legacy reasons, the AST is not valid but // preserve existing behavior. return !isLiteralValue(assignTarget, true); } } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - constructor call node */ static boolean constructorCallHasSideEffects(Node callNode) { return constructorCallHasSideEffects(callNode, null); } static boolean constructorCallHasSideEffects( Node callNode, AbstractCompiler compiler) { if (!callNode.isNew()) { throw new IllegalStateException( "Expected NEW node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } if (callNode.isOnlyModifiesArgumentsCall() && allArgsUnescapedLocal(callNode)) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.isName() && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } // A list of built-in object creation or primitive type cast functions that // can also be called as constructors but lack side-effects. // TODO(johnlenz): consider adding an extern annotation for this. private static final Set<String> BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS = ImmutableSet.of( "Object", "Array", "String", "Number", "Boolean", "RegExp", "Error"); private static final Set<String> OBJECT_METHODS_WITHOUT_SIDEEFFECTS = ImmutableSet.of("toString", "valueOf"); private static final Set<String> REGEXP_METHODS = ImmutableSet.of("test", "exec"); private static final Set<String> STRING_REGEXP_METHODS = ImmutableSet.of("match", "replace", "search", "split"); /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects(Node callNode) { return functionCallHasSideEffects(callNode, null); } /** * Returns true if calls to this function have side effects. * * @param callNode The call node to inspected. * @param compiler A compiler object to provide program state changing *

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> context information. Can be null. */ static boolean functionCallHasSideEffects( Node callNode, @Nullable AbstractCompiler compiler) { if (!callNode.isCall()) { throw new IllegalStateException( "Expected CALL node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } if (callNode.isOnlyModifiesArgumentsCall() && allArgsUnescapedLocal(callNode)) { return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.isName()) { String name = nameNode.getString(); if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) { return false; } } else if (nameNode.isGetProp()) { if (callNode.hasOneChild() && OBJECT_METHODS_WITHOUT_SIDEEFFECTS.contains( nameNode.getLastChild().getString())) { return false; } if (callNode.isOnlyModifiesThisCall() && evaluatesToLocalValue(nameNode.getFirstChild())) { return false; } // Math.floor has no side-effects. // TODO(nicksantos): This is a terrible terrible hack, until // I create a definitionProvider that understands namespacing. if (nameNode.getFirstChild().isName()) { if ("Math.floor".equals(nameNode.getQualifiedName())) { return false; } } if (compiler != null && !compiler.hasRegExpGlobalReferences()) { if (nameNode.getFirstChild().isRegExp() && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { return false; } else if (nameNode.getFirstChild().isString() && STRING_REGEXP_METHODS.contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.isString() || param.isRegExp())) { return false; } } } } return true; } /** * @return Whether the call has a local result. */ static boolean callHasLocalResult(Node n) { Preconditions.checkState(n.isCall()); return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0; } /** * @return Whether the new has a local result. */ static boolean newHasLocalResult(Node n) { Preconditions.checkState(n.isNew()); return n.isOnlyModifiesThisCall(); } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { return nodeTypeMayHaveSideEffects(n, null); } static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { if (isAssignmentOp(n)) { return true; }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> switch(n.getType()) { case Token.DELPROP: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.CALL: return NodeUtil.functionCallHasSideEffects(n, compiler); case Token.NEW: return NodeUtil.constructorCallHasSideEffects(n, compiler); case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } static boolean allArgsUnescapedLocal(Node callOrNew) { for (Node arg = callOrNew.getFirstChild().getNext(); arg != null; arg = arg.getNext()) { if (!evaluatesToLocalValue(arg)) { return false; } } return true; } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { int precedence = precedenceWithDefault(type); if (precedence !=

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> -1) { return precedence; } throw new Error("Unknown precedence for " + Token.name(type) + " (type " + type + ")"); } static int precedenceWithDefault(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return 3; case Token.AND: return 4; case Token.BITOR: return 5; case Token.BITXOR: return 6; case Token.BITAND: return 7; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return 8; case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: return 9; case Token.LSH: case Token.RSH: case Token.URSH: return 10; case Token.SUB: case Token.ADD: return 11; case Token.MUL: case Token.MOD: case Token.DIV: return 12; case Token.INC: case Token.DEC: case Token.NEW: case Token.DELPROP: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.CALL: case Token.GETELEM: case Token.GETPROP: // Data values case Token.ARRAYLIT: case Token.EMPTY: // TODO(johnlenz): remove this. case Token.FALSE: case Token.FUNCTION: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.STRING: case Token.STRING_KEY: case Token.THIS: case Token.TRUE: return 15; case Token.CAST: return 16; default: // Statements are lower precedence than expressions. return -1; } } static boolean isUndefined(Node n) { switch (n.getType()) { case Token.VOID: return true; case Token.NAME: return n.getString().equals("undefined"); } return false; } static boolean isNullOrUndefined(Node n) { return n.isNull() || isUndefined(n); } static final Predicate<Node> IMMUTABLE_PREDICATE = new Predicate<Node>() { @Override public boolean apply(Node n) { return isImmutableValue(n

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>); } }; static boolean isImmutableResult(Node n) { return allResultsMatch(n, IMMUTABLE_PREDICATE); } /** * Apply the supplied predicate against * all possible result Nodes of the expression. */ static boolean allResultsMatch(Node n, Predicate<Node> p) { switch (n.getType()) { case Token.CAST: return allResultsMatch(n.getFirstChild(), p); case Token.ASSIGN: case Token.COMMA: return allResultsMatch(n.getLastChild(), p); case Token.AND: case Token.OR: return allResultsMatch(n.getFirstChild(), p) && allResultsMatch(n.getLastChild(), p); case Token.HOOK: return allResultsMatch(n.getFirstChild().getNext(), p) && allResultsMatch(n.getLastChild(), p); default: return p.apply(n); } } /** * Apply the supplied predicate against * all possible result Nodes of the expression. */ static boolean anyResultsMatch(Node n, Predicate<Node> p) { switch (n.getType()) { case Token.CAST: return anyResultsMatch(n.getFirstChild(), p); case Token.ASSIGN: case Token.COMMA: return anyResultsMatch(n.getLastChild(), p); case Token.AND: case Token.OR: return anyResultsMatch(n.getFirstChild(), p) || anyResultsMatch(n.getLastChild(), p); case Token.HOOK: return anyResultsMatch(n.getFirstChild().getNext(), p) || anyResultsMatch(n.getLastChild(), p); default: return p.apply(n); } } static class NumbericResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return isNumericResultHelper(n); } } static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE = new NumbericResultPredicate(); /** * Returns true if the result of node evaluation is always a number */ static boolean isNumericResult(Node n) { return allResultsMatch(n, NUMBERIC_RESULT_PREDICATE); } static boolean isNumericResultHelper(Node n) { switch (n.getType()) { case Token.ADD: return !mayBeString(n.getFirstChild()) && !mayBeString(n.getLastChild()); case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.MUL: case Token.MOD: case Token.DIV: case Token.INC: case Token.DEC: case Token.POS: case Token.NEG: case Token.NUMBER: return true; case Token.NAME: String name = n.getString(); if (name.equals("NaN")) { return true; } if (name.equals("Infinity")) { return true; } return false; default: return false; } } static class BooleanResultPredicate implements Predicate<Node> {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> @Override public boolean apply(Node n) { return isBooleanResultHelper(n); } } static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE = new BooleanResultPredicate(); /** * @return Whether the result of node evaluation is always a boolean */ static boolean isBooleanResult(Node n) { return allResultsMatch(n, BOOLEAN_RESULT_PREDICATE); } static boolean isBooleanResultHelper(Node n) { switch (n.getType()) { // Primitives case Token.TRUE: case Token.FALSE: // Comparisons case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: // Queries case Token.IN: case Token.INSTANCEOF: // Inversion case Token.NOT: // delete operator returns a boolean. case Token.DELPROP: return true; default: return false; } } static class MayBeStringResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return mayBeStringHelper(n); } } static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE = new MayBeStringResultPredicate(); /** * @returns Whether the results is possibly a string. */ static boolean mayBeString(Node n) { return mayBeString(n, true); } static boolean mayBeString(Node n, boolean recurse) { if (recurse) { return anyResultsMatch(n, MAY_BE_STRING_PREDICATE); } else { return mayBeStringHelper(n); } } static boolean mayBeStringHelper(Node n) { return !isNumericResult(n) && !isBooleanResult(n) && !isUndefined(n) && !n.isNull(); } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token.BITXOR: case Token.BITAND: return true; default: return false; } } /** * Returns true if the operator is commutative. * e.g. (a * b) * c = c * (b * a) * Note 1: "+" is not commutative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 * Note 2: only operations on literals and pure functions are commutative. */ static boolean isCommutative(int type) { switch (type) { case Token.MUL: case

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node */ static boolean isObjectLitKey(Node node) { switch (node.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected when using the key. */ static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { if (valueType != null) { switch (key.getType()) { case Token.GETTER_DEF: // GET must always return a function type. if (valueType.isFunctionType()) { FunctionType fntype = valueType.toMaybeFunctionType(); valueType = fntype.getReturnType(); } else { return null; } break; case Token.SETTER_DEF: if (valueType.isFunctionType()) { // SET must always return a function type. FunctionType fntype = valueType.toMaybeFunctionType(); Node param = fntype.getParametersNode().getFirstChild(); // SET function must always have one parameter. valueType = param.getJSType(); } else { return null; } break; } } return valueType; } /** * Determines whether a node represents an object literal get or set key * (e.g. key1 in {get key1() {}, set key2(a){}). * * @param node A node */ static boolean isGetOrSetKey(Node node) { switch (node.getType()) { case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Converts an operator's token value (see {@link Token}) to a string * representation. * * @param operator the operator's token value to convert * @return the string representation or {@code null} if the token value is * not an operator */ static String opToStr(int operator) { switch (operator) { case Token.BITOR: return "|"; case Token.OR: return "||"; case Token.BITXOR: return "^"; case Token.AND: return "&&"; case Token.BITAND: return "&"; case Token.SHEQ: return "==="; case Token.EQ: return "=="; case Token.NOT: return "!"; case Token.NE: return "!="; case Token.SHNE: return "!=="; case Token.LSH: return "<<"; case Token.IN: return "in"; case Token.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>LE: return "<="; case Token.LT: return "<"; case Token.URSH: return ">>>"; case Token.RSH: return ">>"; case Token.GE: return ">="; case Token.GT: return ">"; case Token.MUL: return "*"; case Token.DIV: return "/"; case Token.MOD: return "%"; case Token.BITNOT: return "~"; case Token.ADD: return "+"; case Token.SUB: return "-"; case Token.POS: return "+"; case Token.NEG: return "-"; case Token.ASSIGN: return "="; case Token.ASSIGN_BITOR: return "|="; case Token.ASSIGN_BITXOR: return "^="; case Token.ASSIGN_BITAND: return "&="; case Token.ASSIGN_LSH: return "<<="; case Token.ASSIGN_RSH: return ">>="; case Token.ASSIGN_URSH: return ">>>="; case Token.ASSIGN_ADD: return "+="; case Token.ASSIGN_SUB: return "-="; case Token.ASSIGN_MUL: return "*="; case Token.ASSIGN_DIV: return "/="; case Token.ASSIGN_MOD: return "%="; case Token.VOID: return "void"; case Token.TYPEOF: return "typeof"; case Token.INSTANCEOF: return "instanceof"; default: return null; } } /** * Converts an operator's token value (see {@link Token}) to a string * representation or fails. * * @param operator the operator's token value to convert * @return the string representation * @throws Error if the token value is not an operator */ static String opToStrNoFail(int operator) { String res = opToStr(operator); if (res == null) { throw new Error("Unknown op " + operator + ": " + Token.name(operator)); } return res; } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = IR.var( IR.name(nameNode.getString()) .srcref(nameNode)) .srcref(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Node nameNode = newName(convention, name, basisNode); nameNode.putProp(Node.ORIGINALNAME_PROP, originalName); return nameNode; } /** Test if all characters in the string are in the Basic Latin (aka ASCII) * character set - that they have UTF-16 values equal to or below 0x7f. * This check can find which identifiers with Unicode characters need to be * escaped in order to allow resulting files to be processed by non-Unicode * aware UNIX tools and editors. * * * See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode * for more on Basic Latin. * * @param s The string to be checked for ASCII-goodness. * * @return True if all characters in the string are in Basic Latin set. */ static boolean isLatin(String s) { int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name is a valid variable name. */ static boolean isValidSimpleName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, Unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. isLatin(name); } /** * Determines whether the given name is a valid qualified name. */ // TODO(nicksantos): This should be moved into a "Language" API, // so that the results are different for es5 and es3. public static boolean isValidQualifiedName(String name) { if (name.endsWith(".") || name.startsWith(".")) { return false; } String[] parts = name.split("\\."); for (String part : parts) { if (!isValidSimpleName(part)) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return isValidSimpleName(name); } private static class VarCollector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); @Override public void visit(Node n) { if (n.isName()) { Node parent = n.getParent(); if (parent != null && parent.isVar()) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root,

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> collector, MATCH_NOT_FUNCTION); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { return isExprAssign(n) && isPrototypeProperty(n.getFirstChild().getFirstChild()); } /** * @return Whether the node represents a qualified prototype property. */ static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); return lhsString != null && lhsString.contains(".prototype."); } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (cur.isGetProp()) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { Node node = IR.voidNode(IR.number(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = IR.name(name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.srcref(value); } Node var = IR.var(nodeName).srcref(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } @Override public boolean apply(Node n) { return n.isName() && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } @Override public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { @Override public boolean apply(Node n) { return isFunctionDeclaration(n) || n.isVar(); } } /** * A predicate for matching anything except function nodes. */

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> private static class MatchNotFunction implements Predicate<Node>{ @Override public boolean apply(Node n) { return !n.isFunction(); } } static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction(); /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ @Override public boolean apply(Node n) { Node parent = n.getParent(); return n.isBlock() || (!n.isFunction() && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount( Node node, int type, Predicate<Node> traverseChildrenPred) { return getCount(node, new MatchNodeType(type), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNameNode(name), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name) { return isNameReferenced(node, name, Predicates.<Node>alwaysTrue()); } /** * Finds the number of times a simple name is referenced within the node tree. */ static int getNameReferenceCount(Node node, String name) { return getCount( node, new MatchNameNode(name), Predicates.<Node>alwaysTrue()); } /** * @return Whether the predicate is true for the node or any of its children. */ static boolean has(Node node, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) { if (pred.apply(node)) { return true; } if (!traverseChildrenPred.apply(node)) { return false; } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { if (has(c, pred, traverseChildrenPred)) { return true; } } return false; } /** * @return The number of times the the predicate is true for the node * or any of its children. */ static int getCount( Node n, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) { int total = 0; if (pred.apply(n)) { total++; } if (traverseChildrenPred.apply(n)) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { total += getCount(c, pred, traverseChildrenPred); } } return total; } /** * Interface for use with the visit method. * @see #visit */ static interface Visitor { void visit(Node node); } /** * A pre-order traversal, calling Visitor.visit for each child matching * the predicate. */ static void visitPreOrder(Node node, Visitor visitor, Predicate<Node> traverseChildrenPred) { visitor.visit(node);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, visitor, traverseChildrenPred); } } } /** * A post-order traversal, calling Visitor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor visitor, Predicate<Node> traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, visitor, traverseChildrenPred); } } visitor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.isTry()); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.isTry()); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.isBlock()); return n.hasChildren() && n.getFirstChild().isCatch(); } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ public static Node getFunctionParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } /** * <p>Determines whether a variable is constant: * <ol> * <li>In Normalize, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * </ol> * * @param node A NAME or STRING node * @return True if a name node represents a constant variable */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { if (parent.isGetProp() && node == parent.getLastChild()) { return convention.isConstantKey(node.getString()); } else if (isObjectLitKey(node)) { return convention.isConstantKey(node.getString()); } else if (node.isName()) { return convention.isConstant(node.getString()); } return false; } /** * Get the JSDocInfo for a function. */ public static JSDocInfo getFunctionJSDocInfo(Node n) { Preconditions.checkState(n.isFunction()); JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> && NodeUtil.isFunctionExpression(n)) { // Look for the info on other nodes. Node parent = n.getParent(); if (parent.isAssign()) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.isName()) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } return fnInfo; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ public static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = n.getSourceFileName(); n = n.getParent(); } return sourceName; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ public static StaticSourceFile getSourceFile(Node n) { StaticSourceFile sourceName = null; while (sourceName == null && n != null) { sourceName = n.getStaticSourceFile(); n = n.getParent(); } return sourceName; } /** * @param n The node. * @return The InputId property on the node or its ancestors. */ public static InputId getInputId(Node n) { while (n != null && !n.isScript()) { n = n.getParent(); } return (n != null && n.isScript()) ? n.getInputId() : null; } /** * A new CALL node with the "FREE_CALL" set based on call target. */ static Node newCallNode(Node callTarget, Node... parameters) { boolean isFreeCall = !isGet(callTarget); Node call = IR.call(callTarget); call.putBooleanProp(Node.FREE_CALL, isFreeCall); for (Node parameter : parameters) { call.addChildToBack(parameter); } return call; } /** * @return Whether the node is known to be a value that is not referenced * elsewhere. */ static boolean evaluatesToLocalValue(Node value) { return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse()); } /** * @param locals A predicate to apply to unknown local values. * @return Whether the node is known to be a value that is not a reference * outside the expression scope. */ static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) { switch (value.getType()) { case Token.CAST: return evaluatesToLocalValue(value.getFirstChild(), locals); case Token.ASSIGN: // A result that is aliased by a non-local name, is the effectively the // same as returning a non-local name, but this doesn't matter if the // value is immutable. return NodeUtil.isImmutableValue(value.getLastChild()) || (locals.apply(value) && evaluatesToLocalValue(value.getLastChild(), locals)); case Token.COMMA: return evaluatesToLocalValue(value.getLastChild(), locals); case Token.AND: case Token.OR: return evaluates

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>ToLocalValue(value.getFirstChild(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.HOOK: return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.INC: case Token.DEC: if (value.getBooleanProp(Node.INCRDECR_PROP)) { return evaluatesToLocalValue(value.getFirstChild(), locals); } else { return true; } case Token.THIS: return locals.apply(value); case Token.NAME: return isImmutableValue(value) || locals.apply(value); case Token.GETELEM: case Token.GETPROP: // There is no information about the locality of object properties. return locals.apply(value); case Token.CALL: return callHasLocalResult(value) || isToStringMethodCall(value) || locals.apply(value); case Token.NEW: return newHasLocalResult(value) || locals.apply(value); case Token.FUNCTION: case Token.REGEXP: case Token.ARRAYLIT: case Token.OBJECTLIT: // Literals objects with non-literal children are allowed. return true; case Token.DELPROP: case Token.IN: // TODO(johnlenz): should IN operator be included in #isSimpleOperator? return true; default: // Other op force a local value: // x = '' + g (x is now an local string) // x -= g (x is now an local number) if (isAssignmentOp(value) || isSimpleOperator(value) || isImmutableValue(value)) { return true; } throw new IllegalStateException( "Unexpected expression node" + value + "\n parent:" + value.getParent()); } } /** * Given the first sibling, this returns the nth * sibling or null if no such sibling exists. * This is like "getChildAtIndex" but returns null for non-existent indexes. */ private static Node getNthSibling(Node first, int index) { Node sibling = first; while (index != 0 && sibling != null) { sibling = sibling.getNext(); index--; } return sibling; } /** * Given the function, this returns the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(function.isFunction()); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } /** * Returns whether this is a target of a call or new. */ static boolean isCallOrNewTarget(Node target) { Node parent = target.getParent(); return parent != null && NodeUtil.isCallOrNew(parent) && parent.getFirstChild()

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Nodes is false, we are comparing the initial AST to the // final AST. Don't check unmarked nodes b/c they may have been changed by // non-loopable passes. // If verifyUnchangedNodes is true, we are comparing the ASTs before & after // a pass. Check all scope roots. final Map<Node, Node> mtoc = map; final boolean checkUnchanged = verifyUnchangedNodes; Node clone = mtoc.get(main); if (main.getChangeTime() > clone.getChangeTime()) { Preconditions.checkState(!main.isEquivalentToShallow(clone)); } else if (checkUnchanged) { Preconditions.checkState(main.isEquivalentToShallow(clone)); } visitPreOrder(main, new Visitor() { @Override public void visit(Node n) { if (n.isFunction() && mtoc.containsKey(n)) { Node clone = mtoc.get(n); if (n.getChangeTime() > clone.getChangeTime()) { Preconditions.checkState(!n.isEquivalentToShallow(clone)); } else if (checkUnchanged) { Preconditions.checkState(n.isEquivalentToShallow(clone)); } } } }, Predicates.<Node>alwaysTrue()); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.EnumType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import javax.annotation.Nullable; /** * Set the JSDocInfo on all types. * * Propagates JSDoc across the type graph, but not across the symbol graph. * This means that if you have: * <code> * var x = new Foo(); * x.bar; * </code> * then the JSType attached to x.bar may get associated JSDoc, but the * Node and Var will not. * * JSDoc is initially attached to AST Nodes at parse time. * There are 3 ways that JSDoc get propagated across the type system. * 1) Nominal types (e.g., constructors) may contain JSDocInfo for their * declaration. * 2) Object types have a JSDocInfo slot for each property on that type. * 3) Shape types (like structural functions) may have JSDocInfo. * * #1 and #2 should be self-explanatory, and non-controversial. #3 is * a bit trickier. It means that if you have: * <code> * /** @param {number} x / * Foo.prototype.bar = goog.abstractMethod; * </code> * the JSDocInfo will appear in two places in the type system: in the 'bar' * slot of Foo.prototype, and on the function expression type created by * this expression. * * @author nicksantos@google.com (Nick Santos) */ class InferJSDocInfo extends AbstractPostOrderCallback implements HotSwapCompilerPass { private final AbstractCompiler compiler; @SuppressWarnings("unused") private boolean inExterns; InferJSDocInfo(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (externs != null) { inExterns = true; Node

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Traversal.traverse(compiler, externs, this); } if (root != null) { inExterns = false; NodeTraversal.traverse(compiler, root, this); } } @Override public void hotSwapScript(Node root, Node originalRoot) { Preconditions.checkNotNull(root); Preconditions.checkState(root.isScript()); inExterns = false; NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo docInfo; switch (n.getType()) { // Infer JSDocInfo on types of all type declarations on variables. case Token.NAME: if (parent == null) { return; } // Only allow JSDoc on VARs, function declarations, and assigns. if (!parent.isVar() && !NodeUtil.isFunctionDeclaration(parent) && !(parent.isAssign() && n == parent.getFirstChild())) { return; } // There are four places the doc info could live. // 1) A FUNCTION node. // /** ... */ function f() { ... } // 2) An ASSIGN parent. // /** ... */ x = function () { ... } // 3) A NAME parent. // var x, /** ... */ y = function() { ... } // 4) A VAR gramps. // /** ... */ var x = function() { ... } docInfo = n.getJSDocInfo(); if (docInfo == null && !(parent.isVar() && !parent.hasOneChild())) { docInfo = parent.getJSDocInfo(); } // Try to find the type of the NAME. JSType varType = n.getJSType(); if (varType == null && parent.isFunction()) { varType = parent.getJSType(); } // If we have no type to attach JSDocInfo to, then there's nothing // we can do. if (varType == null || docInfo == null) { return; } // Dereference the type. If the result is not an object, or already // has docs attached, then do nothing. ObjectType objType = dereferenceToObject(varType); if (objType == null || objType.getJSDocInfo() != null) { return; } attachJSDocInfoToNominalTypeOrShape(objType, docInfo, n.getString()); break; case Token.GETPROP: // Infer JSDocInfo on properties. // There are two ways to write doc comments on a property. // // 1) // /** @deprecated */ // obj.prop = ... // // 2) // /** @deprecated */ // obj.prop; if (parent.isExprResult() || (parent.isAssign() && parent.getFirstChild() == n)) { docInfo = n.getJSDocInfo(); if (docInfo == null) { docInfo = parent.getJSDocInfo(); } if (docInfo != null) { ObjectType lhsType = dereferenceToObject(n.getFirstChild().getJS

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> of all the properties of this record type. * @param declared Whether this is a declared or synthesized type. * A synthesized record type is just used for bookkeeping * in the type system. A declared record type was actually used in the * user's program. * @throws IllegalStateException if the {@code RecordProperty} associated * with a property is null. */ RecordType(JSTypeRegistry registry, Map<String, RecordProperty> properties, boolean declared) { super(registry, null, null); setPrettyPrint(true); this.declared = declared; for (String property : properties.keySet()) { RecordProperty prop = properties.get(property); if (prop == null) { throw new IllegalStateException( "RecordProperty associated with a property should not be null!"); } if (declared) { defineDeclaredProperty( property, prop.getType(), prop.getPropertyNode()); } else { defineSynthesizedProperty( property, prop.getType(), prop.getPropertyNode()); } } // Freeze the record type. isFrozen = true; } /** @return Is this synthesized for internal bookkeeping? */ boolean isSynthetic() { return !declared; } boolean checkRecordEquivalenceHelper( RecordType otherRecord, EquivalenceMethod eqMethod) { Set<String> keySet = getOwnPropertyNames(); Set<String> otherKeySet = otherRecord.getOwnPropertyNames(); if (!otherKeySet.equals(keySet)) { return false; } for (String key : keySet) { if (!otherRecord.getPropertyType(key).checkEquivalenceHelper( getPropertyType(key), eqMethod)) { return false; } } return true; } @Override public ObjectType getImplicitPrototype() { return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { if (isFrozen) { return false; } return super.defineProperty(propertyName, type, inferred, propertyNode); } JSType getGreatestSubtypeHelper(JSType that) { if (that.isRecordType()) { RecordType thatRecord = that.toMaybeRecordType(); RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.setSynthesized(true); // The greatest subtype consists of those *unique* properties of both // record types. If any property conflicts, then the NO_TYPE type // is returned. for (String property : getOwnPropertyNames()) { if (thatRecord.hasProperty(property) && !thatRecord.getPropertyType(property).isInvariant( getPropertyType(property))) { return registry.getNativeObjectType(JSTypeNative.NO_TYPE); } builder.addProperty(property, getPropertyType(property), getPropertyNode(property)); } for (String property : thatRecord.getOwnPropertyNames()) { if (!hasProperty(property)) { builder.addProperty(property, thatRecord.getPropertyType(property), thatRecord.getPropertyNode(property)); } } return builder.build(); } JSType greatestSubtype = registry.getNativeType( JSTypeNative.NO

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>_OBJECT_TYPE); JSType thatRestrictedToObj = registry.getNativeType(JSTypeNative.OBJECT_TYPE) .getGreatestSubtype(that); if (!thatRestrictedToObj.isEmptyType()) { // In this branch, the other type is some object type. We find // the greatest subtype with the following algorithm: // 1) For each property "x" of this record type, take the union // of all classes with a property "x" with a compatible property type. // and which are a subtype of {@code that}. // 2) Take the intersection of all of these unions. for (String propName : getOwnPropertyNames()) { JSType propType = getPropertyType(propName); UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (ObjectType alt : registry.getEachReferenceTypeWithProperty(propName)) { JSType altPropType = alt.getPropertyType(propName); if (altPropType != null && !alt.isEquivalentTo(this) && alt.isSubtype(that) && propType.isInvariant(altPropType)) { builder.addAlternate(alt); } } greatestSubtype = greatestSubtype.getLeastSupertype(builder.build()); } } return greatestSubtype; } @Override RecordType toMaybeRecordType() { return this; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } // Top of the record types is the empty record, or OBJECT_TYPE. if (registry.getNativeObjectType( JSTypeNative.OBJECT_TYPE).isSubtype(that)) { return true; } // A type is a subtype of a record type if it itself is a record // type and it has at least the same members as the parent record type // with the same types. if (!that.isRecordType()) { return false; } return RecordType.isSubtype(this, that.toMaybeRecordType()); } /** Determines if typeA is a subtype of typeB */ static boolean isSubtype(ObjectType typeA, RecordType typeB) { // typeA is a subtype of record type typeB iff: // 1) typeA has all the properties declared in typeB. // 2) And for each property of typeB, // 2a) if the property of typeA is declared, it must be equal // to the type of the property of typeB, // 2b) otherwise, it must be a subtype of the property of typeB. // // To figure out why this is true, consider the following pseudo-code: // /** @type {{a: (Object,null)}} */ var x; // /** @type {{a: !Object}} */ var y; // var z = {a: {}}; // x.a = null; // // y cannot be assigned to x, because line 4 would violate y's declared // properties. But z can be assigned to x. Even though z and y are the // same type, the properties of z are inferred--and so an assignment // to the property of z would not violate any restrictions

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * Scope contains information about a variable scope in JavaScript. * Scopes can be nested, a scope points back to its parent scope. * A Scope contains information about variables defined in that scope. * <p> * A Scope is also used as a lattice element for flow-sensitive type inference. * As a lattice element, a Scope is viewed as a map from names to types. A name * not in the map is considered to have the bottom type. The join of two maps m1 * and m2 is the map of the union of names with {@link JSType#getLeastSupertype} * to meet the m1 type and m2 type. * * @see NodeTraversal * @see DataFlowAnalysis * */ public class Scope implements StaticScope<JSType>, StaticSymbolTable<Scope.Var, Scope.Var> { private final Map<String, Var> vars = new LinkedHashMap<String, Var>(); private final Scope parent; private final int depth; private final Node rootNode; /** Whether this is a bottom scope for the purposes of type inference. */ private final boolean isBottom; private Var arguments; private static final Predicate<Var> DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES = new Predicate<Var>() { @Override public boolean apply(Var var) { return var.getParentNode() != null && var.getType() == null && // no declared type var.getParentNode().is

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Var() && !var.isExtern(); } }; /** Stores info about a variable */ public static class Var implements StaticSlot<JSType>, StaticReference<JSType> { /** name */ final String name; /** Var node */ final Node nameNode; /** * The variable's type. */ private JSType type; /** * Whether the variable's type has been inferred or is declared. An inferred * type may change over time (as more code is discovered), whereas a * declared type is a static contract that must be matched. */ private final boolean typeInferred; /** Input source */ final CompilerInput input; /** * The index at which the var is declared. e..g if it's 0, it's the first * declared variable in that scope */ final int index; /** The enclosing scope */ final Scope scope; /** @see #isMarkedEscaped */ private boolean markedEscaped = false; /** @see #isMarkedAssignedExactlyOnce */ private boolean markedAssignedExactlyOnce = false; /** * Creates a variable. * * @param inferred whether its type is inferred (as opposed to declared) */ private Var(boolean inferred, String name, Node nameNode, JSType type, Scope scope, int index, CompilerInput input) { this.name = name; this.nameNode = nameNode; this.type = type; this.scope = scope; this.index = index; this.input = input; this.typeInferred = inferred; } /** * Gets the name of the variable. */ @Override public String getName() { return name; } /** * Gets the node for the name of the variable. */ @Override public Node getNode() { return nameNode; } CompilerInput getInput() { return input; } @Override public StaticSourceFile getSourceFile() { return nameNode.getStaticSourceFile(); } @Override public Var getSymbol() { return this; } @Override public Var getDeclaration() { return nameNode == null ? null : this; } /** * Gets the parent of the name node. */ public Node getParentNode() { return nameNode == null ? null : nameNode.getParent(); } /** * Whether this is a bleeding function (an anonymous named function * that bleeds into the inner scope). */ public boolean isBleedingFunction() { return NodeUtil.isFunctionExpression(getParentNode()); } /** * Gets the scope where this variable is declared. */ Scope getScope() { return scope; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>code NodeUtil}. */ public boolean isConst() { return nameNode != null && NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotated by {@code @define}. */ public boolean isDefine() { JSDocInfo info = getJSDocInfo(); return info != null && info.isDefine(); } public Node getInitialValue() { return NodeUtil.getRValueOfLValue(nameNode); } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isTypeInferred()}. */ @Override public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ @Override public JSDocInfo getJSDocInfo() { return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode); } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Resolve this variable's type. */ void resolveType(ErrorReporter errorReporter) { if (type != null) { type = type.resolve(errorReporter, scope); } } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ @Override public boolean isTypeInferred() { return typeInferred; } public String getInputName() { if (input == null) { return "<non-file>"; } return input.getName(); } public boolean isNoShadow() { JSDocInfo info = getJSDocInfo(); return info != null && info.isNoShadow(); } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name + "{" + type + "}"; } /** * Record that this is escaped by an inner scope. * * In other words, it's assigned in an inner scope so that it's much harder * to make assertions about its value at a given point. */ void markEscaped() { markedEscaped = true; } /** * Whether this is escaped by an inner scope. * Notice that not all scope creators record this information. */ boolean isMarkedEscaped() { return markedEscaped; } /** * Record that this is assigned exactly once.. * * In other words, it's assigned in an inner scope so that it's much harder * to make assertions about its value at a given point.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> */ void markAssignedExactlyOnce() { markedAssignedExactlyOnce = true; } /** * Whether this is assigned exactly once. * Notice that not all scope creators record this information. */ boolean isMarkedAssignedExactlyOnce() { return markedAssignedExactlyOnce; } } /** * A special subclass of Var used to distinguish "arguments" in the current * scope. */ // TODO(johnlenz): Include this the list of Vars for the scope. public static class Arguments extends Var { Arguments(Scope scope) { super( false, // no inferred "arguments", // always arguments null, // no declaration node // TODO(johnlenz): provide the type of "Arguments". null, // no type info scope, -1, // no variable index null // input ); } @Override public boolean equals(Object other) { if (!(other instanceof Arguments)) { return false; } Arguments otherVar = (Arguments) other; return otherVar.scope.getRootNode() == scope.getRootNode(); } @Override public int hashCode() { return System.identityHashCode(this); } } /** * Creates a Scope given the parent Scope and the root node of the scope. * @param parent The parent Scope. Cannot be null. * @param rootNode Typically the FUNCTION node. */ Scope(Scope parent, Node rootNode) { Preconditions.checkNotNull(parent); Preconditions.checkArgument(rootNode != parent.rootNode); this.parent = parent; this.rootNode = rootNode; this.isBottom = false; this.depth = parent.depth + 1; } /** * Creates a empty Scope (bottom of the lattice). * @param rootNode Typically a FUNCTION node or the global BLOCK node. * @param isBottom Whether this is the bottom of a lattice. Otherwise, * it must be a global scope. */ private Scope(Node rootNode, boolean isBottom) { this.parent = null; this.rootNode = rootNode; this.isBottom = isBottom; this.depth = 0; } static Scope createGlobalScope(Node rootNode) { return new Scope(rootNode, false); } static Scope createLatticeBottom(Node rootNode) { return new Scope(rootNode, true); } /** The depth of the scope. The global scope has depth 0. */ int getDepth() { return depth; } /** Whether this is the bottom of the lattice. */ boolean isBottom() { return isBottom; } /** * Gets the container node of the scope. This is typically the FUNCTION * node or the global BLOCK/SCRIPT node. */ @Override public Node getRootNode() { return rootNode; } public Scope getParent() { return parent; } Scope getGlobalScope() { Scope result = this; while (result.getParent() != null) { result = result.getParent(); } return result; } @Override public StaticScope<JSType> getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>. */ @Override public JSType getTypeOfThis() { if (isGlobal()) { return ObjectType.cast(rootNode.getJSType()); } Preconditions.checkState(rootNode.isFunction()); JSType nodeType = rootNode.getJSType(); if (nodeType != null && nodeType.isFunctionType()) { return nodeType.toMaybeFunctionType().getTypeOfThis(); } else { return parent.getTypeOfThis(); } } /** * Declares a variable whose type is inferred. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input); vars.put(name, var); return var; } /** * Undeclares a variable, to be used when the compiler optimizes out * a variable and removes it from the scope. */ void undeclare(Var var) { Preconditions.checkState(var.scope == this); Preconditions.checkState(vars.get(var.name) == var); vars.remove(var.name); } @Override public Var getSlot(String name) { return getVar(name); } @Override public Var getOwnSlot(String name) { return vars.get(name); } /** * Returns the variable, may be null */ public Var getVar(String name) { Var var = vars.get(name); if (var != null) { return var; } else if (parent != null) { // Recurse up the parent Scope return parent.getVar(name); } else { return null; } } /** * Get a unique VAR object to represents "arguments" within this scope */ public Var getArgumentsVar() { if (arguments == null) { arguments = new Arguments(this); } return arguments; } /** * Returns true if a variable is declared. */ public boolean isDeclared(String name, boolean recurse) { Scope scope = this; if (scope.vars.containsKey(name)) { return true; } if (scope.parent != null && recurse) { return scope.parent.isDeclared(name, recurse); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> return false; } /** * Return an iterator over all of the variables declared in this scope. */ public Iterator<Var> getVars() { return vars.values().iterator(); } /** * Return an iterable over all of the variables declared in this scope. */ Iterable<Var> getVarIterable() { return vars.values(); } @Override public Iterable<Var> getReferences(Var var) { return ImmutableList.of(var); } @Override public StaticScope<JSType> getScope(Var var) { return var.scope; } @Override public Iterable<Var> getAllSymbols() { return Collections.unmodifiableCollection(vars.values()); } /** * Returns number of variables in this scope */ public int getVarCount() { return vars.size(); } /** * Returns whether this is the global scope. */ public boolean isGlobal() { return parent == null; } /** * Returns whether this is a local scope (i.e. not the global scope). */ public boolean isLocal() { return parent != null; } /** * Gets all variables declared with "var" but without declared types attached. */ public Iterator<Var> getDeclarativelyUnboundVarsWithoutTypes() { return Iterators.filter( getVars(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES); } static interface TypeResolver { void resolveTypes(); } private TypeResolver typeResolver; /** Resolve all type references. Only used on typed scopes. */ void resolveTypes() { if (typeResolver != null) { typeResolver.resolveTypes(); typeResolver = null; } } void setTypeResolver(TypeResolver resolver) { this.typeResolver = resolver; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; import com.google.javascript.rhino.ErrorReporter; /** * All type, representing all values. */ public final class AllType extends JSType { private static final long serialVersionUID = 1L; AllType(JSTypeRegistry registry) { super(registry); } @Override public boolean isAllType() { return true; } @Override public boolean matchesStringContext() { // Be lenient. return true; } @Override public boolean matchesObjectContext() { // Be lenient. return true; } @Override public boolean canBeCalled() { return false; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override String toStringHelper(boolean forAnnotations) { return "*"; } @Override public String getDisplayName() { return "<Any Type>"; } @Override public boolean hasDisplayName() { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseAllType(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseAllType(that); } @Override public BooleanLiteral

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Set getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import java.io.Serializable; /** * An id used uniquely identify a CompilerInput * @author johnlenz@google.com (John Lenz) */ public class InputId implements Serializable { public static final long serialVersionUID = 1L; private final String id; public InputId(String id) { this.id = id; } public String getIdName() { return id; } @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; return id.equals(((InputId) obj).id); } @Override public String toString() { return "InputId: " + getIdName(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> this.validNodeTypeA = validNodeTypeA; this.validNodeTypeB = validNodeTypeB; this.registerFunction = registerFunction; } boolean isValidNodeType(int type) { return type == validNodeTypeA || type == validNodeTypeB; } boolean isCorrectRegisterFunction(TweakFunction registerFunction) { Preconditions.checkNotNull(registerFunction); return this.registerFunction == registerFunction; } boolean isGetterFunction() { return registerFunction != null; } String getName() { return name; } String getExpectedTypeName() { return expectedTypeName; } Node createDefaultValueNode() { switch (this) { case REGISTER_BOOLEAN: return IR.falseNode(); case REGISTER_NUMBER: return IR.number(0); case REGISTER_STRING: return IR.string(""); default: throw new IllegalStateException(); } } } // A map of function name -> TweakFunction. private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP; static { TWEAK_FUNCTIONS_MAP = Maps.newHashMap(); for (TweakFunction func : TweakFunction.values()) { TWEAK_FUNCTIONS_MAP.put(func.getName(), func); } } ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks, Map<String, Node> compilerDefaultValueOverrides) { this.compiler = compiler; this.stripTweaks = stripTweaks; // Having the map sorted is required for the unit tests to be deterministic. this.compilerDefaultValueOverrides = Maps.newTreeMap(); this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides); } @Override public void process(Node externs, Node root) { CollectTweaksResult result = collectTweaks(root); applyCompilerDefaultValueOverrides(result.tweakInfos); boolean changed = false; if (stripTweaks) { changed = stripAllCalls(result.tweakInfos); } else if (!compilerDefaultValueOverrides.isEmpty()) { changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls); } if (changed) { compiler.reportCodeChange(); } } /** * Passes the compiler default value overrides to the JS by replacing calls * to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value; */ private boolean replaceGetCompilerOverridesCalls( List<TweakFunctionCall> calls) { for (TweakFunctionCall call : calls) { Node callNode = call.callNode; Node objNode = createCompilerDefaultValueOverridesVarNode(callNode); callNode.getParent().replaceChild(callNode, objNode); } return !calls.isEmpty(); } /** * Removes all CALL nodes in the given TweakInfos, replacing calls to getter * functions with the tweak's default value. */ private boolean stripAllCalls(Map<String, TweakInfo> tweakInfos) { for (TweakInfo tweakInfo : tweakInfos.values()) { boolean isRegistered = tweakInfo.isRegistered(); for (TweakFunctionCall functionCall : tweakInfo.functionCalls) { Node callNode = functionCall.callNode; Node parent

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>weakInfo> tweakInfos; final List<TweakFunctionCall> getOverridesCalls; CollectTweaksResult(Map<String, TweakInfo> tweakInfos, List<TweakFunctionCall> getOverridesCalls) { this.tweakInfos = tweakInfos; this.getOverridesCalls = getOverridesCalls; } } /** * Processes all calls to goog.tweak functions. */ private final class CollectTweaks extends AbstractPostOrderCallback { final Map<String, TweakInfo> allTweaks = Maps.newHashMap(); final List<TweakFunctionCall> getOverridesCalls = Lists.newArrayList(); @SuppressWarnings("incomplete-switch") @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isCall()) { return; } String callName = n.getFirstChild().getQualifiedName(); TweakFunction tweakFunc = TWEAK_FUNCTIONS_MAP.get(callName); if (tweakFunc == null) { return; } if (tweakFunc == TweakFunction.GET_COMPILER_OVERRIDES) { getOverridesCalls.add( new TweakFunctionCall(t.getSourceName(), tweakFunc, n)); return; } // Ensure the first parameter (the tweak ID) is a string literal. Node tweakIdNode = n.getFirstChild().getNext(); if (!tweakIdNode.isString()) { compiler.report(t.makeError(tweakIdNode, NON_LITERAL_TWEAK_ID_ERROR)); return; } String tweakId = tweakIdNode.getString(); // Make sure there is a TweakInfo structure for it. TweakInfo tweakInfo = allTweaks.get(tweakId); if (tweakInfo == null) { tweakInfo = new TweakInfo(tweakId); allTweaks.put(tweakId, tweakInfo); } switch (tweakFunc) { case REGISTER_BOOLEAN: case REGISTER_NUMBER: case REGISTER_STRING: // Ensure the ID contains only valid characters. if (!ID_MATCHER.matchesAllOf(tweakId)) { compiler.report(t.makeError(tweakIdNode, INVALID_TWEAK_ID_ERROR)); } // Ensure tweaks are registered in the global scope. if (!t.inGlobalScope()) { compiler.report( t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); break; } // Ensure tweaks are registered only once. if (tweakInfo.isRegistered()) { compiler.report( t.makeError(n, TWEAK_MULTIPLY_REGISTERED_ERROR, tweakId)); break; } Node tweakDefaultValueNode = tweakIdNode.getNext().getNext(); tweakInfo.addRegisterCall(t.getSourceName(), tweakFunc, n, tweakDefaultValueNode); break; case OVERRIDE_DEFAULT_VALUE: // Ensure tweaks overrides occur in the global scope. if (!t.inGlobalScope()) { compiler.report( t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); break; } // Ensure tweak overrides occur before the

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; /** * The type of individual elements of an enum type * (see {@link EnumType}). */ public class EnumElementType extends ObjectType { private static final long serialVersionUID = 1L; /** * The primitive type this enum element type wraps. For instance, in * the following code defining the {@code LOCAL_CODES} enum * <pre>var LOCAL_CODES = {A: 3, B: 9, C: 8}</pre> * the primitive type of the the constants is {@code number}. */ private JSType primitiveType; // The primitive type, if it is an object. private ObjectType primitiveObjectType; private final String name; EnumElementType(JSTypeRegistry registry, JSType elementType, String name) { super(registry); this.primitiveType = elementType; this.primitiveObjectType = elementType.toObjectType(); this.name = name; } @Override public PropertyMap getPropertyMap() { return primitiveObjectType == null ? PropertyMap.immutableEmptyMap() : primitiveObjectType.getPropertyMap(); } @Override public EnumElementType toMaybeEnumElementType() { return this; } @Override public boolean matchesNumberContext() { return primitiveType.matchesNumberContext(); } @Override

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> public boolean matchesStringContext() { return primitiveType.matchesStringContext(); } @Override public boolean matchesObjectContext() { return primitiveType.matchesObjectContext(); } @Override public boolean canBeCalled() { return primitiveType.canBeCalled(); } @Override public boolean isObject() { return primitiveType.isObject(); } @Override public TernaryValue testForEquality(JSType that) { return primitiveType.testForEquality(that); } /** * This predicate determines whether objects of this type can have the null * value, and therefore can appear in contexts where null is expected. * * @return true for everything but Number and Boolean types. */ @Override public boolean isNullable() { return primitiveType.isNullable(); } @Override public boolean isNominalType() { return hasReferenceName(); } /** * If this is equal to a NamedType object, its hashCode must be equal * to the hashCode of the NamedType object. */ @Override public int hashCode() { if (hasReferenceName()) { return getReferenceName().hashCode(); } else { return super.hashCode(); } } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? primitiveType.toString() : (getReferenceName() + ".<" + primitiveType + ">"); } @Override public String getReferenceName() { return name; } @Override public boolean hasReferenceName() { return true; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return primitiveType.isSubtype(that); } } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseEnumElementType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseEnumElementType(this, that); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public JSType findPropertyType(String propertyName) { return primitiveType.findPropertyType(propertyName); } @Override public FunctionType getConstructor() { return primitiveObjectType == null ? null : primitiveObjectType.getConstructor(); } @Override public JSType autoboxesTo() { return primitiveType.autoboxesTo(); } /** * Gets the primitive type of this enum element. */ public JSType getPrimitiveType() { return primitiveType; } /** * Returns the infimum of a enum element type and another type, or null * if the infimum is empty. * * This can be a little bit weird. For example, suppose you have an enum * of {(string|number)}, and you want the greatest subtype of the enum * and a {number}. * * The infimum is non-empty. But at the same time, we don't really have * a name for this infimum. It's equivalent to

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> "elements of this enum that * are numbers". * * The best we can do is make up a new type. This is similar to what * we do in UnionType#meet, which kind-of-sort-of makes sense, because * an EnumElementType is a union of instances of a type. */ JSType meet(JSType that) { JSType meetPrimitive = primitiveType.getGreatestSubtype(that); if (meetPrimitive.isEmptyType()) { return null; } else { return new EnumElementType(registry, meetPrimitive, name); } } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { primitiveType = primitiveType.resolve(t, scope); primitiveObjectType = ObjectType.cast(primitiveType); return this; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; /** * A simple {@link ErrorReporter} that collects warnings and errors and makes * them accessible via {@link #errors()} and {@link #warnings()}. * */ public class SimpleErrorReporter implements ErrorReporter { private List<String> warnings = null; private List<String> errors = null; @Override public void warning(String message, String sourceName, int line, int lineOffset) { if (warnings == null) { warnings = new ArrayList<String>(); } warnings.add(formatDetailedMessage(message, sourceName, line)); } @Override public void error(String message, String sourceName, int line, int lineOffset) { if (errors == null) { errors = new ArrayList<String>(); } errors.add(formatDetailedMessage(message, sourceName, line)); } /** * Returns the list of errors, or {@code null} if there were none. */ public List<String> errors() { return errors; } /** * Returns the list of warnings, or {@code null} if there were none. */ public List<String> warnings() { return warnings; } private String formatDetailedMessage( String

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>SC_TEMPLATE_TYPE_DUPLICATED", "Only one parameter type must be the template type"); static final DiagnosticType TEMPLATE_TYPE_EXPECTED = DiagnosticType.warning( "JSC_TEMPLATE_TYPE_EXPECTED", "The template type must be a parameter type"); static final DiagnosticType THIS_TYPE_NON_OBJECT = DiagnosticType.warning( "JSC_THIS_TYPE_NON_OBJECT", "@this type of a function must be an object\n" + "Actual type: {0}"); static final DiagnosticType SAME_INTERFACE_MULTIPLE_IMPLEMENTS = DiagnosticType.warning( "JSC_SAME_INTERFACE_MULTIPLE_IMPLEMENTS", "Cannot @implement the same interface more than once\n" + "Repeated interface: {0}"); private class ExtendedTypeValidator implements Predicate<JSType> { @Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportWarning(EXTENDS_NON_OBJECT, formatFnName(), type.toString()); return false; } else if (objectType.isEmptyType()) { reportWarning(RESOLVED_TAG_EMPTY, "@extends", formatFnName()); return false; } else if (objectType.isUnknownType()) { if (hasMoreTagsToResolve(objectType)) { return true; } else { reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); return false; } } else { return true; } } } private class ImplementedTypeValidator implements Predicate<JSType> { @Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportError(BAD_IMPLEMENTED_TYPE, fnName); return false; } else if (objectType.isEmptyType()) { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); return false; } else if (objectType.isUnknownType()) { if (hasMoreTagsToResolve(objectType)) { return true; } else { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); return false; } } else { return true; } } } /** * @param fnName The function name. * @param compiler The compiler. * @param errorRoot The node to associate with any warning generated by * this builder. * @param sourceName A source name for associating any warnings that * we have to emit. * @param scope The syntactic scope. */ FunctionTypeBuilder(String fnName, AbstractCompiler compiler, Node errorRoot, String sourceName, Scope scope) { Preconditions.checkNotNull(errorRoot); this.fnName = fnName == null ? "" : fnName; this.codingConvention = compiler.getCodingConvention(); this.typeRegistry = compiler.getTypeRegistry(); this.errorRoot = errorRoot; this.sourceName = sourceName; this.compiler = compiler; this.scope = scope; } /** Format the function name for use in warnings. */ String formatFnName() { return fnName.isEmpty() ? "<anonymous>" : fn

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Expr = fromInlineDoc ? info.getType() : info.getReturnType(); if (returnTypeExpr != null) { returnType = returnTypeExpr.evaluate(scope, typeRegistry); returnTypeInferred = false; } } return this; } /** * Infer the role of the function (whether it's a constructor or interface) * and what it inherits from in JSDocInfo. */ FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) { if (info != null) { isConstructor = info.isConstructor(); makesStructs = info.makesStructs(); makesDicts = info.makesDicts(); isInterface = info.isInterface(); if (makesStructs && !isConstructor) { reportWarning(CONSTRUCTOR_REQUIRED, "@struct", formatFnName()); } else if (makesDicts && !isConstructor) { reportWarning(CONSTRUCTOR_REQUIRED, "@dict", formatFnName()); } // Class template types, which can be used in the scope of a constructor // definition. ImmutableList<String> typeParameters = info.getTemplateTypeNames(); if (!typeParameters.isEmpty()) { if (isConstructor || isInterface) { ImmutableList.Builder<TemplateType> builder = ImmutableList.builder(); for (String typeParameter : typeParameters) { builder.add(typeRegistry.createTemplateType(typeParameter)); } classTemplateTypeNames = builder.build(); typeRegistry.setTemplateTypeNames(classTemplateTypeNames); } } // base type if (info.hasBaseType()) { if (isConstructor) { JSType maybeBaseType = info.getBaseType().evaluate(scope, typeRegistry); if (maybeBaseType != null && maybeBaseType.setValidator(new ExtendedTypeValidator())) { baseType = (ObjectType) maybeBaseType; } } else { reportWarning(EXTENDS_WITHOUT_TYPEDEF, formatFnName()); } } // Implemented interfaces (for constructors only). if (info.getImplementedInterfaceCount() > 0) { if (isConstructor) { implementedInterfaces = Lists.newArrayList(); Set<JSType> baseInterfaces = new HashSet<JSType>(); for (JSTypeExpression t : info.getImplementedInterfaces()) { JSType maybeInterType = t.evaluate(scope, typeRegistry); if (maybeInterType != null && maybeInterType.setValidator(new ImplementedTypeValidator())) { // Disallow implementing the same base (not templatized) interface // type more than once. JSType baseInterface = maybeInterType; if (baseInterface.toMaybeTemplatizedType() != null) { baseInterface = baseInterface.toMaybeTemplatizedType().getReferencedType(); } if (baseInterfaces.contains(baseInterface)) { reportWarning(SAME_INTERFACE_MULTIPLE_IMPLEMENTS, baseInterface.toString()); } else { baseInterfaces.add(baseInterface); } implementedInterfaces.add((ObjectType) maybeInterType); } } } else if (isInterface) { reportWarning( TypeCheck.CONFLICTING_IMPLEMENTED_TYPE, formatFnName()); } else { reportWarning(CONSTRUCTOR_REQUIRED, "@implements", formatFnName

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> = null; boolean isOptionalParam = isOptionalParameter(arg, info); isVarArgs = isVarArgsParameter(arg, info); if (info != null && info.hasParameterType(argumentName)) { parameterType = info.getParameterType(argumentName).evaluate(scope, typeRegistry); } else if (arg.getJSDocInfo() != null && arg.getJSDocInfo().hasType()) { parameterType = arg.getJSDocInfo().getType().evaluate(scope, typeRegistry); } else if (oldParameterType != null && oldParameterType.getJSType() != null) { parameterType = oldParameterType.getJSType(); isOptionalParam = oldParameterType.isOptionalArg(); isVarArgs = oldParameterType.isVarArgs(); } else { parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE); } warnedAboutArgList |= addParameter( builder, parameterType, warnedAboutArgList, isOptionalParam, isVarArgs); if (oldParameterType != null) { oldParameterType = oldParameterType.getNext(); } } // Copy over any old parameters that aren't in the param list. if (!isVarArgs) { while (oldParameterType != null && !isVarArgs) { builder.newParameterFromNode(oldParameterType); oldParameterType = oldParameterType.getNext(); } } for (String inexistentName : allJsDocParams) { reportWarning(INEXISTANT_PARAM, inexistentName, formatFnName()); } parametersNode = builder.build(); return this; } /** * @return Whether the given param is an optional param. */ private boolean isOptionalParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isOptionalParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isOptionalArg(); } /** * Determine whether this is a var args parameter. * @return Whether the given param is a var args param. */ private boolean isVarArgsParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isVarArgsParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isVarArgs(); } /** * Infer the template type from the doc info. */ FunctionTypeBuilder inferTemplateTypeName( @Nullable JSDocInfo info, JSType ownerType) { // NOTE: these template type names may override a list // of inherited ones from an overridden function. if (info != null && !info.getTemplateTypeNames().isEmpty()) { ImmutableList.Builder<TemplateType> builder = ImmutableList.builder(); for (String key : info.getTemplateTypeNames()) { builder.add(typeRegistry.createTemplateType(key)); } templateTypeNames = builder.build(); } ImmutableList<TemplateType> keys = templateTypeNames; if (ownerType != null) { ImmutableList<TemplateType> ownerTypeKeys =

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Names(); /** Gets a list of variables whose properties are escaped. */ Set<String> getEscapedQualifiedNames(); /** Gets the number of times each variable has been assigned. */ Multiset<String> getAssignedNameCounts(); } static class UnknownFunctionContents implements FunctionContents { private static UnknownFunctionContents singleton = new UnknownFunctionContents(); static FunctionContents get() { return singleton; } @Override public Node getSourceNode() { return null; } @Override public boolean mayBeFromExterns() { return true; } @Override public boolean mayHaveNonEmptyReturns() { return true; } @Override public boolean mayHaveSingleThrow() { return true; } @Override public Iterable<String> getEscapedVarNames() { return ImmutableList.of(); } @Override public Set<String> getEscapedQualifiedNames() { return ImmutableSet.of(); } @Override public Multiset<String> getAssignedNameCounts() { return ImmutableMultiset.of(); } } static class AstFunctionContents implements FunctionContents { private final Node n; private boolean hasNonEmptyReturns = false; private Set<String> escapedVarNames; private Set<String> escapedQualifiedNames; private final Multiset<String> assignedVarNames = HashMultiset.create(); AstFunctionContents(Node n) { this.n = n; } @Override public Node getSourceNode() { return n; } @Override public boolean mayBeFromExterns() { return n.isFromExterns(); } @Override public boolean mayHaveNonEmptyReturns() { return hasNonEmptyReturns; } void recordNonEmptyReturn() { hasNonEmptyReturns = true; } @Override public boolean mayHaveSingleThrow() { Node block = n.getLastChild(); return block.hasOneChild() && block.getFirstChild().isThrow(); } @Override public Iterable<String> getEscapedVarNames() { return escapedVarNames == null ? ImmutableList.<String>of() : escapedVarNames; } void recordEscapedVarName(String name) { if (escapedVarNames == null) { escapedVarNames = Sets.newHashSet(); } escapedVarNames.add(name); } @Override public Set<String> getEscapedQualifiedNames() { return escapedQualifiedNames == null ? ImmutableSet.<String>of() : escapedQualifiedNames; } void recordEscapedQualifiedName(String name) { if (escapedQualifiedNames == null) { escapedQualifiedNames = Sets.newHashSet(); } escapedQualifiedNames.add(name); } @Override public Multiset<String> getAssignedNameCounts() { return assignedVarNames; } void recordAssignedName(String name) { assignedVarNames.add(name); } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.jscomp.GlobalNamespace.Name; import com.google.javascript.jscomp.GlobalNamespace.Ref; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.util.Set; /** * Checks references to undefined properties of global variables. * * @author nicksantos@google.com (Nick Santos) */ class CheckGlobalNames implements CompilerPass { private final AbstractCompiler compiler; private final CodingConvention convention; private final CheckLevel level; private GlobalNamespace namespace = null; private final Set<String> objectPrototypeProps = Sets.newHashSet(); private final Set<String> functionPrototypeProps = Sets.newHashSet(); // Warnings static final DiagnosticType UNDEFINED_NAME_WARNING = DiagnosticType.warning( "JSC_UNDEFINED_NAME", "{0} is never defined"); static final DiagnosticType NAME_DEFINED_LATE_WARNING = DiagnosticType.warning( "JSC_NAME_DEFINED_LATE", "{0} defined before its owner. {1} is defined at {2}:{3}"); static final DiagnosticType STRICT_MODULE_DEP_QNAME = DiagnosticType.disabled( "JSC_STRICT_MODULE_DEP_QNAME", "module {0} cannot reference {2}, defined in " + "module {1}"); /** * Creates a pass to check global name references at the given warning level. */ CheckGlobalNames(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.convention = compiler.getCodingConvention(); this.level = level; } /** * Injects a pre-computed global namespace, so that the same namespace * can be re-used for multiple check passes. Returns this for easy chaining. */ CheckGlobalNames injectNamespace(GlobalNamespace namespace) { Preconditions.checkArgument(namespace.hasExternsRoot()); this.namespace = namespace; return this; } @Override public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, externs, root); } // Find prototype properties that will affect our analysis. Preconditions.checkState(namespace.hasExternsRoot()); findPrototypeProps("Object", objectPrototypeProps); findPrototypeProps("Function", functionPrototypeProps); objectPrototypeProps.addAll( convention.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>() && (convention.getClassesDefinedByCall(aliaser) != null || convention.getSingletonGetterClassName(aliaser) != null); if (!isKnownAlias) { parentIsAliased = true; } } } } if (parentIsAliased) { return false; } if (objectPrototypeProps.contains(name.getBaseName())) { return false; } if (name.parent.type == Name.Type.OBJECTLIT) { return true; } if (name.parent.type == Name.Type.FUNCTION && name.parent.isDeclaredType() && !functionPrototypeProps.contains(name.getBaseName())) { return true; } return false; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Collection; import java.util.List; import java.util.Set; /** * This describes the Closure-specific JavaScript coding conventions. * */ public class ClosureCodingConvention extends CodingConventions.Proxy { private static final long serialVersionUID = 1L; static final DiagnosticType OBJECTLIT_EXPECTED = DiagnosticType.warning( "JSC_REFLECT_OBJECTLIT_EXPECTED", "Object literal expected as second argument"); private final Set<String> indirectlyDeclaredProperties; public ClosureCodingConvention() { this(CodingConventions.getDefault()); } public ClosureCodingConvention(CodingConvention wrapped) { super(wrapped); Set<String> props = Sets.newHashSet( "superClass_", "instance_", "getInstance"); props.addAll(wrapped.getIndirectlyDeclaredProperties()); indirectlyDeclaredProperties = ImmutableSet.copyOf(props); } /** * Closure's goog.inherits adds a {@code superClass_} property to the * subclass, and a {@code constructor} property. */ @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { super.applySubclassRelationship(parentCtor, childCtor, type); if (type == SubclassType.INHERITS) { childCtor.defineDeclaredProperty("superClass_", parentCtor.getPrototype(), childCtor.getSource()); childCtor.getPrototype().defineDeclaredProperty("constructor", // Notice that constructor functions do not need to be covariant // on the superclass. // So if G extends F, new G() and new F() can accept completely // different argument types, but G.prototype.constructor needs // to be covariant on F.prototype.constructor. // To get around this, we just turn off type-checking on arguments // and return types of

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> G.prototype.constructor. childCtor.cloneWithoutArrowType(), childCtor.getSource()); } } /** * {@inheritDoc} * * <p>Understands several different inheritance patterns that occur in * Google code (various uses of {@code inherits} and {@code mixin}). */ @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { SubclassRelationship relationship = super.getClassesDefinedByCall(callNode); if (relationship != null) { return relationship; } Node callName = callNode.getFirstChild(); SubclassType type = typeofClassDefiningName(callName); if (type != null) { Node subclass = null; Node superclass = callNode.getLastChild(); // There are six possible syntaxes for a class-defining method: // SubClass.inherits(SuperClass) // goog.inherits(SubClass, SuperClass) // goog$inherits(SubClass, SuperClass) // SubClass.mixin(SuperClass.prototype) // goog.mixin(SubClass.prototype, SuperClass.prototype) // goog$mixin(SubClass.prototype, SuperClass.prototype) boolean isDeprecatedCall = callNode.getChildCount() == 2 && callName.isGetProp(); if (isDeprecatedCall) { // SubClass.inherits(SuperClass) subclass = callName.getFirstChild(); } else if (callNode.getChildCount() == 3) { // goog.inherits(SubClass, SuperClass) subclass = callName.getNext(); } else { return null; } if (type == SubclassType.MIXIN) { // Only consider mixins that mix two prototypes as related to // inheritance. if (!endsWithPrototype(superclass)) { return null; } if (!isDeprecatedCall) { if (!endsWithPrototype(subclass)) { return null; } // Strip off the prototype from the name. subclass = subclass.getFirstChild(); } superclass = superclass.getFirstChild(); } // bail out if either of the side of the "inherits" // isn't a real class name. This prevents us from // doing something weird in cases like: // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) if (subclass != null && subclass.isUnscopedQualifiedName() && superclass.isUnscopedQualifiedName()) { return new SubclassRelationship(type, subclass, superclass); } } return null; } /** * Determines whether the given node is a class-defining name, like * "inherits" or "mixin." * @return The type of class-defining name, or null. */ private SubclassType typeofClassDefiningName(Node callName) { // Check if the method name matches one of the class-defining methods. String methodName = null; if (callName.isGetProp()) { methodName = callName.getLastChild().getString(); } else if (callName.isName()) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) { return "superClass_".equals(propertyName) || super.isSuperClassReference(propertyName); } /** * Given a qualified name node, returns whether "prototype" is at the end. * For example: * a.b.c => false * a.b.c.prototype => true */ private boolean endsWithPrototype(Node qualifiedName) { return qualifiedName.isGetProp() && qualifiedName.getLastChild().getString().equals("prototype"); } /** * Extracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Extracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.isGetProp()) { String qualifiedName = callee.getQualifiedName(); if (functionName.equals(qualifiedName)) { Node target = callee.getNext(); if (target != null && target.isString()) { className = target.getString(); } } } } return className; } /** * Use closure's implementation. * @return closure's function name for exporting properties. */ @Override public String getExportPropertyFunction() { return "goog.exportProperty"; } /** * Use closure's implementation. * @return closure's function name for exporting symbols. */ @Override public String getExportSymbolFunction() { return "goog.exportSymbol"; } @Override public List<String> identifyTypeDeclarationCall(Node n) { Node callName = n.getFirstChild(); if ("goog.addDependency".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.isArrayLit()) { List<String> typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.isString()) { typeNames.add(name.getString()); } } return typeNames; } } return super.identifyTypeDeclarationCall(n); } @Override public String getAbstractMethodName() { return "goog.abstractMethod"; }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> @Override public String getSingletonGetterClassName(Node callNode) { Node callArg = callNode.getFirstChild(); String callName = callArg.getQualifiedName(); // Use both the original name and the post-CollapseProperties name. if (!("goog.addSingletonGetter".equals(callName) || "goog$addSingletonGetter".equals(callName)) || callNode.getChildCount() != 2) { return super.getSingletonGetterClassName(callNode); } return callArg.getNext().getQualifiedName(); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { super.applySingletonGetter(functionType, getterType, objectType); functionType.defineDeclaredProperty("getInstance", getterType, functionType.getSource()); functionType.defineDeclaredProperty("instance_", objectType, functionType.getSource()); } @Override public String getGlobalObject() { return "goog.global"; } private final Set<String> propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.isCall()); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()) || super.isPropertyTestFunction(call); } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { Preconditions.checkArgument(callNode.isCall()); ObjectLiteralCast proxyCast = super.getObjectLiteralCast(callNode); if (proxyCast != null) { return proxyCast; } Node callName = callNode.getFirstChild(); if (!"goog.reflect.object".equals(callName.getQualifiedName()) || callNode.getChildCount() != 3) { return null; } Node typeNode = callName.getNext(); if (!typeNode.isQualifiedName()) { return null; } Node objectNode = typeNode.getNext(); if (!objectNode.isObjectLit()) { return new ObjectLiteralCast(null, null, OBJECTLIT_EXPECTED); } return new ObjectLiteralCast( typeNode.getQualifiedName(), typeNode.getNext(), null); } @Override public boolean isOptionalParameter(Node parameter) { return false; } @Override public boolean isVarArgsParameter(Node parameter) { return false; } @Override public boolean isPrivate(String name) { return false; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return ImmutableList.<AssertionFunctionSpec>of( new AssertionFunctionSpec("goog.asserts.assert"), new AssertionFunctionSpec("goog.asserts.assertNumber", JSTypeNative.NUMBER_TYPE), new AssertionFunctionSpec("goog.asserts.assertString", JSTypeNative.STRING_TYPE), new AssertionFunctionSpec("goog.asserts.assertFunction", JSTypeNative.FUNCTION_INSTANCE_TYPE), new AssertionFunctionSpec("goog.asserts.assertObject",

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> JSTypeNative.OBJECT_TYPE), new AssertionFunctionSpec("goog.asserts.assertArray", JSTypeNative.ARRAY_TYPE), new AssertFunctionByTypeName("goog.asserts.assertElement", "Element"), new AssertInstanceofSpec("goog.asserts.assertInstanceof") ); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { Bind result = super.describeFunctionBind(n, useTypeInfo); if (result != null) { return result; } if (!n.isCall()) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name.equals("goog.bind") || name.equals("goog$bind")) { // goog.bind(fn, self, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = safeNext(fn); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } if (name.equals("goog.partial") || name.equals("goog$partial")) { // goog.partial(fn, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = null; Node parameters = safeNext(fn); return new Bind(fn, thisValue, parameters); } } return null; } @Override public Collection<String> getIndirectlyDeclaredProperties() { return indirectlyDeclaredProperties; } private Node safeNext(Node n) { if (n != null) { return n.getNext(); } return null; } /** * A function that will throw an exception when if the value is not * an instanceof a specific type. */ public static class AssertInstanceofSpec extends AssertionFunctionSpec { public AssertInstanceofSpec(String functionName) { super(functionName, JSTypeNative.OBJECT_TYPE); } /** * Returns the type for a type assertion, or null if the function asserts * that the node must not be null or undefined. */ @Override public JSType getAssertedType(Node call, JSTypeRegistry registry) { if (call.getChildCount() > 2) { Node constructor = call.getFirstChild().getNext().getNext(); if (constructor != null) { JSType ownerType = constructor.getJSType(); if (ownerType != null && ownerType.isFunctionType() && ownerType.isConstructor()) { FunctionType functionType = ((FunctionType) ownerType); return functionType.getInstanceType(); } } } return super.getAssertedType(call, registry); } } /** * A function that will throw an exception when the value is not an * instanceof the given type name, for instance "Element". */ public static class AssertFunctionByTypeName extends AssertionFunctionSpec { private final String typeName; public AssertFunctionByTypeName(String functionName, String typeName) { super(functionName); this.typeName = typeName; } /** {@inheritDoc} */ @Override public

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ModificationVisitor; import com.google.javascript.rhino.jstype.TemplateType; import com.google.javascript.rhino.jstype.TemplateTypeMap; import java.util.ArrayDeque; /** * Uses a TemplateTypeMap to replace TemplateTypes with their associated JSType * values. * * @author izaakr@google.com (Izaak Rubin) */ public class TemplateTypeMapReplacer extends ModificationVisitor { private final TemplateTypeMap replacements; private ArrayDeque<TemplateType> visitedTypes; public TemplateTypeMapReplacer( JSTypeRegistry registry, TemplateTypeMap replacements) { super(registry); this.replacements = replacements; this.visitedTypes = new ArrayDeque<TemplateType>(); } @Override public JSType caseTemplateType(TemplateType type) { if (replacements.hasTemplateKey(type)) { if (hasVisitedType(type) || !replacements.hasTemplateType(type)) { // If we have already encountered this TemplateType during replacement // (i.e. there is a reference loop), or there is no JSType substitution // for the TemplateType, return the

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>: return "is_dispatcher"; case DIRECTIVES: return "directives"; case DIRECT_EVAL: return "direct_eval"; case FREE_CALL: return "free_call"; case STATIC_SOURCE_FILE: return "source_file"; case INPUT_ID: return "input_id"; case LENGTH: return "length"; case SLASH_V: return "slash_v"; case INFERRED_FUNCTION: return "inferred"; case CHANGE_TIME: return "change_time"; case REFLECTED_OBJECT: return "reflected_object"; default: throw new IllegalStateException("unexpected prop id " + propType); } } private static class NumberNode extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override boolean isEquivalentTo( Node node, boolean compareJsType, boolean recur, boolean shallow) { boolean equiv = super.isEquivalentTo(node, compareJsType, recur, shallow); if (equiv) { double thisValue = getDouble(); double thatValue = ((NumberNode) node).getDouble(); if (thisValue == thatValue) { // detect the difference between 0.0 and -0.0. return (thisValue != 0.0) || (1 / thisValue == 1 / thatValue); } } return false; } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** * returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** * sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override boolean isEquivalentTo( Node node, boolean compareJsType, boolean recur, boolean shallow) { return (super.isEquivalentTo(node, compareJsType, recur, shallow) && this.str.equals(((StringNode) node).str)); } /** * If the property is not defined, this was not a quoted key.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> The * QUOTED_PROP int property is only assigned to STRING tokens used as * object lit keys. * @return true if this was a quoted string key in an object literal. */ @Override public boolean isQuotedString() { return getBooleanProp(QUOTED_PROP); } /** * This should only be called for STRING nodes created in object lits. */ @Override public void setQuotedString() { putBooleanProp(QUOTED_PROP, true); } private String str; } // PropListItems must be immutable so that they can be shared. private interface PropListItem { int getType(); PropListItem getNext(); PropListItem chain(PropListItem next); Object getObjectValue(); int getIntValue(); } private abstract static class AbstractPropListItem implements PropListItem, Serializable { private static final long serialVersionUID = 1L; private final PropListItem next; private final int propType; AbstractPropListItem(int propType, PropListItem next) { this.propType = propType; this.next = next; } @Override public int getType() { return propType; } @Override public PropListItem getNext() { return next; } @Override public abstract PropListItem chain(PropListItem next); } // A base class for Object storing props private static class ObjectPropListItem extends AbstractPropListItem { private static final long serialVersionUID = 1L; private final Object objectValue; ObjectPropListItem(int propType, Object objectValue, PropListItem next) { super(propType, next); this.objectValue = objectValue; } @Override public int getIntValue() { throw new UnsupportedOperationException(); } @Override public Object getObjectValue() { return objectValue; } @Override public String toString() { return objectValue == null ? "null" : objectValue.toString(); } @Override public PropListItem chain(PropListItem next) { return new ObjectPropListItem(getType(), objectValue, next); } } // A base class for int storing props private static class IntPropListItem extends AbstractPropListItem { private static final long serialVersionUID = 1L; final int intValue; IntPropListItem(int propType, int intValue, PropListItem next) { super(propType, next); this.intValue = intValue; } @Override public int getIntValue() { return intValue; } @Override public Object getObjectValue() { throw new UnsupportedOperationException(); } @Override public String toString() { return String.valueOf(intValue); } @Override public PropListItem chain(PropListItem next) { return new IntPropListItem(getType(), intValue, next); } } public Node(int nodeType) { type = nodeType; parent = null; sourcePosition = -1; } public Node(int nodeType, Node child) { Preconditions.checkArgument(child.parent == null, "new child has existing parent"); Preconditions.checkArgument(child.next == null, "new child has existing sibling"); type = nodeType; parent = null; first = last = child; child.next = null;

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>this + " is not a number node"); } } /** * Can only be called when <tt>getType() == Token.NUMBER</tt> * @param value value to set. */ public void setDouble(double value) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** * Can only be called for a Token.STRING or Token.NAME. * @param value the value to set. */ public void setString(String value) throws UnsupportedOperationException { if (this.getType() == Token.STRING || this.getType() == Token.NAME) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first == null || first.getType() != Token.NAME) { sb.append("<invalid>"); } else { sb.append(first.getString()); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i = 0; i < keys.length; i++) { int type = keys[i]; PropListItem x = lookupProperty(type); sb.append(" ["); sb.append(propToString(type)); sb.append(": "); String value; switch (type) { default: value = x.toString(); break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString = jsType.toString(); if (jsTypeString != null) { sb

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> (see {@link #mergeLineCharNo(int, int)}). */ protected static int extractLineno(int lineCharNo) { if (lineCharNo == -1) { return -1; } else { return lineCharNo >>> COLUMN_BITS; } } /** * Extracts the character number and character number from a merged line * char number (see {@link #mergeLineCharNo(int, int)}). */ protected static int extractCharno(int lineCharNo) { if (lineCharNo == -1) { return -1; } else { return lineCharNo & COLUMN_MASK; } } //========================================================================== // Iteration /** * <p>Return an iterable object that iterates over this node's children. * The iterator does not support the optional operation * {@link Iterator#remove()}.</p> * * <p>To iterate over a node's children, one can write</p> * <pre>Node n = ...; * for (Node child : n.children()) { ...</pre> */ public Iterable<Node> children() { if (first == null) { return Collections.emptySet(); } else { return new SiblingNodeIterable(first); } } /** * <p>Return an iterable object that iterates over this node's siblings. * The iterator does not support the optional operation * {@link Iterator#remove()}.</p> * * <p>To iterate over a node's siblings, one can write</p> * <pre>Node n = ...; * for (Node sibling : n.siblings()) { ...</pre> */ public Iterable<Node> siblings() { return new SiblingNodeIterable(this); } /** * @see Node#siblings() */ private static final class SiblingNodeIterable implements Iterable<Node>, Iterator<Node> { private final Node start; private Node current; private boolean used; SiblingNodeIterable(Node start) { this.start = start; this.current = start; this.used = false; } @Override public Iterator<Node> iterator() { if (!used) { used = true; return this; } else { // We have already used the current object as an iterator; // we must create a new SiblingNodeIterable based on this // iterable's start node. // // Since the primary use case for Node.children is in for // loops, this branch is extremely unlikely. return (new SiblingNodeIterable(start)).iterator(); } } @Override public boolean hasNext() { return current != null; } @Override public Node next() { if (current == null) { throw new NoSuchElementException(); } try { return current; } finally { current = current.getNext(); } } @Override public void remove() { throw new UnsupportedOperationException(); } } // ========================================================================== // Accessors PropListItem getPropListHeadForTesting() { return propListHead; } public Node getParent() { return parent; } /** * Gets the ancestor node relative to this. *

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> * @param level 0 = this, 1 = the parent, etc. */ public Node getAncestor(int level) { Preconditions.checkArgument(level >= 0); Node node = this; while (node != null && level-- > 0) { node = node.getParent(); } return node; } /** * Iterates all of the node's ancestors excluding itself. */ public AncestorIterable getAncestors() { return new AncestorIterable(this.getParent()); } /** * Iterator to go up the ancestor tree. */ public static class AncestorIterable implements Iterable<Node> { private Node cur; /** * @param cur The node to start. */ AncestorIterable(Node cur) { this.cur = cur; } @Override public Iterator<Node> iterator() { return new Iterator<Node>() { @Override public boolean hasNext() { return cur != null; } @Override public Node next() { if (!hasNext()) { throw new NoSuchElementException(); } Node n = cur; cur = cur.getParent(); return n; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } /** * Check for one child more efficiently than by iterating over all the * children as is done with Node.getChildCount(). * * @return Whether the node has exactly one child. */ public boolean hasOneChild() { return first != null && first == last; } /** * Check for more than one child more efficiently than by iterating over all * the children as is done with Node.getChildCount(). * * @return Whether the node more than one child. */ public boolean hasMoreThanOneChild() { return first != null && first != last; } public int getChildCount() { int c = 0; for (Node n = first; n != null; n = n.next) { c++; } return c; } // Intended for testing and verification only. public boolean hasChild(Node child) { for (Node n = first; n != null; n = n.getNext()) { if (child == n) { return true; } } return false; } /** * Checks if the subtree under this node is the same as another subtree. * Returns null if it's equal, or a message describing the differences. */ public String checkTreeEquals(Node node2) { NodeMismatch diff = checkTreeEqualsImpl(node2); if (diff != null) { return "Node tree inequality:" + "\nTree1:\n" + toStringTree() + "\n\nTree2:\n" + node2.toStringTree() + "\n\nSubtree1: " + diff.nodeA.toStringTree() + "\n\nSubtree2: " + diff.nodeB.toStringTree(); } return null; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ Node

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>THIS_UNMODIFIED | Node.FLAG_NO_THROWS); } /** * Returns true if this node is a function or constructor call that * has no side effects. */ public boolean isNoSideEffectsCall() { return areBitFlagsSet(getSideEffectFlags(), NO_SIDE_EFFECTS); } /** * Returns true if this node is a function or constructor call that * returns a primitive or a local object (an object that has no other * references). */ public boolean isLocalResultCall() { return areBitFlagsSet(getSideEffectFlags(), FLAG_LOCAL_RESULTS); } /** Returns true if this is a new/call that may mutate its arguments. */ public boolean mayMutateArguments() { return !areBitFlagsSet(getSideEffectFlags(), FLAG_ARGUMENTS_UNMODIFIED); } /** Returns true if this is a new/call that may mutate global state or throw. */ public boolean mayMutateGlobalStateOrThrow() { return !areBitFlagsSet(getSideEffectFlags(), FLAG_GLOBAL_STATE_UNMODIFIED | FLAG_NO_THROWS); } /** * returns true if all the flags are set in value. */ private boolean areBitFlagsSet(int value, int flags) { return (value & flags) == flags; } /** * This should only be called for STRING nodes children of OBJECTLIT. */ public boolean isQuotedString() { return false; } /** * This should only be called for STRING nodes children of OBJECTLIT. */ public void setQuotedString() { throw new IllegalStateException("not a StringNode"); } static class NodeMismatch { final Node nodeA; final Node nodeB; NodeMismatch(Node nodeA, Node nodeB) { this.nodeA = nodeA; this.nodeB = nodeB; } @Override public boolean equals(Object object) { if (object instanceof NodeMismatch) { NodeMismatch that = (NodeMismatch) object; return that.nodeA.equals(this.nodeA) && that.nodeB.equals(this.nodeB); } return false; } @Override public int hashCode() { return Objects.hashCode(nodeA, nodeB); } } /*** AST type check methods ***/ public boolean isAdd() { return this.getType() == Token.ADD; } public boolean isAnd() { return this.getType() == Token.AND; } public boolean isArrayLit() { return this.getType() == Token.ARRAYLIT; } public boolean isAssign() { return this.getType() == Token.ASSIGN; } public boolean isAssignAdd() { return this.getType() == Token.ASSIGN_ADD; } public boolean isBlock() { return this.getType() == Token.BLOCK; } public boolean isBreak() { return this.getType() == Token.BREAK; } public boolean isCall() { return this.getType() == Token.CALL; } public boolean isCase() { return this.getType() == Token.CASE; } public boolean isCast() { return this.getType() == Token.CAST; } public

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Number type. */ public class NumberType extends ValueType { private static final long serialVersionUID = 1L; NumberType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNullable() { return false; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; } @Override public boolean isNumberValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> return "number"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNumberType(); } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Preconditions; import com.google.javascript.rhino.Node; /** * An object type that is an instance of some function constructor. */ class InstanceObjectType extends PrototypeObjectType { private static final long serialVersionUID = 1L; private final FunctionType constructor; InstanceObjectType(JSTypeRegistry registry, FunctionType constructor) { this(registry, constructor, false); } InstanceObjectType(JSTypeRegistry registry, FunctionType constructor, boolean isNativeType) { super(registry, null, null, isNativeType, constructor.getTemplateTypeMap()); Preconditions.checkNotNull(constructor); this.constructor = constructor; } @Override public String getReferenceName() { return getConstructor().getReferenceName(); } @Override public boolean hasReferenceName() { return getConstructor().hasReferenceName(); } @Override public ObjectType getImplicitPrototype() { return getConstructor().getPrototype(); } @Override public FunctionType getConstructor() { return constructor; } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { ObjectType proto = getImplicitPrototype(); if (proto != null && proto.hasOwnDeclaredProperty(name)) { return false; } return super.defineProperty(name, type, inferred, propertyNode); } @Override String toStringHelper

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>(boolean forAnnotations) { if (constructor.hasReferenceName()) { return constructor.getReferenceName(); } else { return super.toStringHelper(forAnnotations); } } @Override boolean isTheObjectType() { return getConstructor().isNativeObjectType() && "Object".equals(getReferenceName()); } @Override public boolean isInstanceType() { return true; } @Override public boolean isArrayType() { return getConstructor().isNativeObjectType() && "Array".equals(getReferenceName()); } @Override public boolean isStringObjectType() { return getConstructor().isNativeObjectType() && "String".equals(getReferenceName()); } @Override public boolean isBooleanObjectType() { return getConstructor().isNativeObjectType() && "Boolean".equals(getReferenceName()); } @Override public boolean isNumberObjectType() { return getConstructor().isNativeObjectType() && "Number".equals(getReferenceName()); } @Override public boolean isDateType() { return getConstructor().isNativeObjectType() && "Date".equals(getReferenceName()); } @Override public boolean isRegexpType() { return getConstructor().isNativeObjectType() && "RegExp".equals(getReferenceName()); } @Override public boolean isNominalType() { return hasReferenceName(); } /** * If this is equal to a NamedType object, its hashCode must be equal * to the hashCode of the NamedType object. */ @Override public int hashCode() { if (hasReferenceName()) { return getReferenceName().hashCode(); } else { return super.hashCode(); } } @Override public Iterable<ObjectType> getCtorImplementedInterfaces() { return getConstructor().getImplementedInterfaces(); } @Override public Iterable<ObjectType> getCtorExtendedInterfaces() { return getConstructor().getExtendedInterfaces(); } // The owner will always be a resolved type, so there's no need to set // the constructor in resolveInternal. // (it would lead to infinite loops if we did). // JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import java.util.Map; /** * Named groups of DiagnosticTypes exposed by Compiler. * @author nicksantos@google.com (Nick Santos) */ public class DiagnosticGroups { static final DiagnosticType UNUSED = DiagnosticType.warning("JSC_UNUSED", "{0}"); public DiagnosticGroups() {} private static final Map<String, DiagnosticGroup> groupsByName = Maps.newHashMap(); static DiagnosticGroup registerDeprecatedGroup(String name) { return registerGroup(name, new DiagnosticGroup(name, UNUSED)); } static DiagnosticGroup registerGroup(String name, DiagnosticGroup group) { groupsByName.put(name, group); return group; } static DiagnosticGroup registerGroup(String name, DiagnosticType ... types) { DiagnosticGroup group = new DiagnosticGroup(name, types); groupsByName.put(name, group); return group; } static DiagnosticGroup registerGroup(String name, DiagnosticGroup ... groups) { DiagnosticGroup group = new DiagnosticGroup(name, groups); groupsByName.put(name, group); return group; } /** Get the registered diagnostic groups, indexed by name. */ protected Map<String, DiagnosticGroup> getRegisteredGroups() { return ImmutableMap.copyOf(groupsByName); } /** Find the diagnostic group registered under the given name. */ public DiagnosticGroup forName(String name) { return groupsByName.get(name); } // A bit of a hack to display the available groups on the command-line. // New groups should be added to this list if they are public and should // be listed on the command-line as an available option. // // If a group is suppressible on a per-file basis, it should be added // to parser/ParserConfig.properties static final String DIAGNOSTIC_GROUP_NAMES = "accessControls, ambiguousFunctionDecl, checkEventfulObjectDisposal, " + "checkRegExp, checkStructDictInheritance, checkTypes, checkVars, " + "const, constantProperty, deprecated, duplicateMessage, es3, " + "es5Strict, externsValidation, fileoverviewTags, globalThis, " + "internetExplorerChecks, invalidCasts, misplacedTypeAnnotation, " + "missingProperties, missingProvide, missingRequire, missingReturn," + "nonStandardJsDocs, reportUnknownTypes, suspiciousCode

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Maps; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Insures '@constructor X' has a 'goog.provide("X")' . * */ class CheckProvides implements HotSwapCompilerPass { private final AbstractCompiler compiler; private final CheckLevel checkLevel; private final CodingConvention codingConvention; static final DiagnosticType MISSING_PROVIDE_WARNING = DiagnosticType.disabled( "JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')"); CheckProvides(AbstractCompiler compiler, CheckLevel checkLevel) { this.compiler = compiler; this.checkLevel = checkLevel; this.codingConvention = compiler.getCodingConvention(); } @Override public void process(Node externs, Node root) { hotSwapScript(root, null); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { CheckProvidesCallback callback = new CheckProvidesCallback(codingConvention); new NodeTraversal(compiler, callback).traverse(scriptRoot); } private class CheckProvidesCallback extends AbstractShallowCallback { private final Map<String, Node> provides = Maps.newHashMap(); private final Map<String, Node> ctors = Maps.newHashMap(); private final CodingConvention convention; CheckProvidesCallback(CodingConvention convention){ this.convention = convention; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: String providedClassName = codingConvention.extractClassNameIfProvide(n, parent); if (providedClassName != null) { provides.put(providedClassName, n); } break; case Token.FUNCTION: visitFunctionNode(n, parent); break; case Token.SCRIPT: visitScriptNode(); } } private void visitFunctionNode(Node n, Node parent) { Node name = null; JSDocInfo info = parent.getJSDocInfo(); if (info != null && info.isConstructor()) { name = parent.getFirstChild(); } else { // look to the child, maybe it's

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter; import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.testing.Asserts; import java.util.Arrays; import java.util.List; import java.util.Set; /** * Tests {@link TypeCheck}. * */ public class TypeCheckTest extends CompilerTypeTestCase { private CheckLevel reportMissingOverrides = CheckLevel.WARNING; private static final String SUGGESTION_CLASS = "/** @constructor\n */\n" + "function Suggest() {}\n" + "Suggest.prototype.a = 1;\n" + "Suggest.prototype.veryPossible = 1;\n" + "Suggest.prototype.veryPossible2 = 1;\n"; @Override public void setUp() throws Exception { super.setUp(); reportMissingOverrides = CheckLevel.WARNING; } public void testInitialTypingScope() { Scope s = new TypedScopeCreator(compiler, CodingConventions.getDefault()).createInitialScope( new Node(Token.BLOCK)); assertTypeEquals(ARRAY_FUNCTION_TYPE, s.getVar("Array").getType()); assertTypeEquals(BOOLEAN_OBJECT_FUNCTION_TYPE, s.getVar("Boolean").getType()); assertTypeEquals(DATE_FUNCTION_TYPE, s.getVar("Date").getType()); assertTypeEquals(ERROR_FUNCTION_TYPE, s.getVar("Error").getType()); assertTypeEquals(EVAL_ERROR_FUNCTION_TYPE, s.getVar("EvalError").getType()); assertTypeEquals(NUMBER_OBJECT_FUNCTION_TYPE, s.getVar("Number").getType()); assertTypeEquals(OBJECT_FUNCTION_TYPE, s.getVar("Object").getType()); assertTypeEquals(RANGE_ERROR_

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Null type. */ public final class NullType extends ValueType { private static final long serialVersionUID = 1L; NullType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNullType() { return true; } @Override public boolean isNullable() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public JSType restrictByNotNullOrUndefined() { return registry.getNativeType(JSTypeNative.NO_TYPE); } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isNullType() || that.isVoidType()) { return TRUE; } if (that.isUnknownType() || that.isNullable()) { return

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> UNKNOWN; } return FALSE; } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "null"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.FALSE; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNullType(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayDeque; import java.util.Comparator; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.PriorityQueue; /** * This is a compiler pass that computes a control flow graph. * */ final class ControlFlowAnalysis implements Callback, CompilerPass { /** * Based roughly on the first few pages of * * "Declarative Intraprocedural Flow Analysis of Java Source Code by * Nilsson-Nyman, Hedin, Magnusson & Ekman", * * this pass computes the control flow graph from the AST. However, a full * attribute grammar is not necessary. We will compute the flow edges with a * single post order traversal. The "follow()" of a given node will be * computed recursively in a demand driven fashion. * * As of this moment, we are not performing any inter-procedural analysis * within our framework. */ private final AbstractCompiler compiler; private ControlFlowGraph<Node> cfg; private Map<Node, Integer> astPosition; // TODO(nicksantos): should these be node annotations? private Map<DiGraphNode<Node, Branch>, Integer> nodePriorities; // We order CFG nodes by by looking at the AST positions. // CFG nodes that come first lexically should be visited first, because // they will often be executed first in the source program. private final Comparator<DiGraphNode<Node, Branch>> priorityComparator = new Comparator<DiGraphNode<Node, Branch>>() { @Override public int compare( DiGraphNode<Node, Branch> a, DiGraphNode<Node, Branch> b) { return astPosition.get(a.getValue()) - astPosition.get(b.getValue()); } }; private int astPositionCounter; private int priorityCounter; private final boolean shouldTraverseFunctions;

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> private final boolean edgeAnnotations; // We need to store where we started, in case we aren't doing a flow analysis // for the whole scope. This happens, for example, when running type inference // on only the externs. private Node root; /* * This stack captures the structure of nested TRY blocks. The top of the * stack is the inner most TRY block. A FUNCTION node in this stack implies * that the handler is determined by the caller of the function at runtime. */ private final Deque<Node> exceptionHandler = new ArrayDeque<Node>(); /* * This map is used to handle the follow of FINALLY. For example: * * while(x) { * try { * try { * break; * } catch (a) { * } finally { * foo(); * } * fooFollow(); * } catch (b) { * } finally { * bar(); * } * barFollow(); * } * END(); * * In this case finallyMap will contain a map from: * first FINALLY -> bar() * second FINALLY -> END() * * When we are connecting foo() and bar() to to their respective follow, we * must also look up this map and connect: * foo() -> bar() * bar() -> END */ private final Multimap<Node, Node> finallyMap = HashMultimap.create(); /** * Constructor. * * @param compiler Compiler instance. * @param shouldTraverseFunctions Whether functions should be traversed (true * by default). * @param edgeAnnotations Whether to allow edge annotations. By default, * only node annotations are allowed. */ ControlFlowAnalysis(AbstractCompiler compiler, boolean shouldTraverseFunctions, boolean edgeAnnotations) { this.compiler = compiler; this.shouldTraverseFunctions = shouldTraverseFunctions; this.edgeAnnotations = edgeAnnotations; } ControlFlowGraph<Node> getCfg() { return cfg; } @Override public void process(Node externs, Node root) { this.root = root; astPositionCounter = 0; astPosition = Maps.newHashMap(); nodePriorities = Maps.newHashMap(); cfg = new AstControlFlowGraph(computeFallThrough(root), nodePriorities, edgeAnnotations); NodeTraversal.traverse(compiler, root, this); astPosition.put(null, ++astPositionCounter); // the implicit return is last. // Now, generate the priority of nodes by doing a depth-first // search on the CFG. priorityCounter = 0; DiGraphNode<Node, Branch> entry = cfg.getEntry(); prioritizeFromEntryNode(entry); if (shouldTraverseFunctions) { // If we're traversing inner functions, we need to rank the // priority of them too. for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) { Node value = candidate.getValue(); if (value != null && value.isFunction()) { Preconditions.checkState( !nodePriorities.containsKey(candidate) || candidate == entry); prioritizeFromEntryNode

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>(candidate); } } } // At this point, all reachable nodes have been given a priority, but // unreachable nodes have not been given a priority. Put them last. // Presumably, it doesn't really matter what priority they get, since // this shouldn't happen in real code. for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) { if (!nodePriorities.containsKey(candidate)) { nodePriorities.put(candidate, ++priorityCounter); } } // Again, the implicit return node is always last. nodePriorities.put(cfg.getImplicitReturn(), ++priorityCounter); } /** * Given an entry node, find all the nodes reachable from that node * and prioritize them. */ private void prioritizeFromEntryNode(DiGraphNode<Node, Branch> entry) { PriorityQueue<DiGraphNode<Node, Branch>> worklist = new PriorityQueue<DiGraphNode<Node, Branch>>(10, priorityComparator); worklist.add(entry); while (!worklist.isEmpty()) { DiGraphNode<Node, Branch> current = worklist.remove(); if (nodePriorities.containsKey(current)) { continue; } nodePriorities.put(current, ++priorityCounter); List<DiGraphNode<Node, Branch>> successors = cfg.getDirectedSuccNodes(current); for (DiGraphNode<Node, Branch> candidate : successors) { worklist.add(candidate); } } } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { astPosition.put(n, astPositionCounter++); switch (n.getType()) { case Token.FUNCTION: if (shouldTraverseFunctions || n == cfg.getEntry().getValue()) { exceptionHandler.push(n); return true; } return false; case Token.TRY: exceptionHandler.push(n); return true; } /* * We are going to stop the traversal depending on what the node's parent * is. * * We are only interested in adding edges between nodes that change control * flow. The most obvious ones are loops and IF-ELSE's. A statement * transfers control to its next sibling. * * In case of an expression tree, there is no control flow within the tree * even when there are short circuited operators and conditionals. When we * are doing data flow analysis, we will simply synthesize lattices up the * expression tree by finding the meet at each expression node. * * For example: within a Token.SWITCH, the expression in question does not * change the control flow and need not to be considered. */ if (parent != null) { switch (parent.getType()) { case Token.FOR: // Only traverse the body of the for loop. return n == parent.getLastChild(); // Skip the conditions. case Token.IF: case Token.WHILE: case Token.WITH: return n != parent.getFirstChild(); case Token.DO: return n != parent.getFirstChild().getNext(); // Only traverse the body of the cases case Token.SWITCH: case

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Token.CASE: case Token.CATCH: case Token.LABEL: return n != parent.getFirstChild(); case Token.FUNCTION: return n == parent.getFirstChild().getNext().getNext(); case Token.CONTINUE: case Token.BREAK: case Token.EXPR_RESULT: case Token.VAR: case Token.RETURN: case Token.THROW: return false; case Token.TRY: /* Just before we are about to visit the second child of the TRY node, * we know that we will be visiting either the CATCH or the FINALLY. * In other words, we know that the post order traversal of the TRY * block has been finished, no more exceptions can be caught by the * handler at this TRY block and should be taken out of the stack. */ if (n == parent.getFirstChild().getNext()) { Preconditions.checkState(exceptionHandler.peek() == parent); exceptionHandler.pop(); } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.IF: handleIf(n); return; case Token.WHILE: handleWhile(n); return; case Token.DO: handleDo(n); return; case Token.FOR: handleFor(n); return; case Token.SWITCH: handleSwitch(n); return; case Token.CASE: handleCase(n); return; case Token.DEFAULT_CASE: handleDefault(n); return; case Token.BLOCK: case Token.SCRIPT: handleStmtList(n); return; case Token.FUNCTION: handleFunction(n); return; case Token.EXPR_RESULT: handleExpr(n); return; case Token.THROW: handleThrow(n); return; case Token.TRY: handleTry(n); return; case Token.CATCH: handleCatch(n); return; case Token.BREAK: handleBreak(n); return; case Token.CONTINUE: handleContinue(n); return; case Token.RETURN: handleReturn(n); return; case Token.WITH: handleWith(n); return; case Token.LABEL: return; default: handleStmt(n); return; } } private void handleIf(Node node) { Node thenBlock = node.getFirstChild().getNext(); Node elseBlock = thenBlock.getNext(); createEdge(node, Branch.ON_TRUE, computeFallThrough(thenBlock)); if (elseBlock == null) { createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); // not taken branch } else { createEdge(node, Branch.ON_FALSE, computeFallThrough(elseBlock)); } connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleWhile(Node node) { // Control goes to the first statement if the condition evaluates to true. createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild().getNext())); // Control goes to the

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> /** * A {@link ControlFlowGraph} which provides a node comparator based on the * pre-order traversal of the AST. */ private static class AstControlFlowGraph extends ControlFlowGraph<Node> { private final Map<DiGraphNode<Node, Branch>, Integer> priorities; /** * Constructor. * @param entry The entry node. * @param priorities The map from nodes to position in the AST (to be * filled by the {@link ControlFlowAnalysis#shouldTraverse}). */ private AstControlFlowGraph(Node entry, Map<DiGraphNode<Node, Branch>, Integer> priorities, boolean edgeAnnotations) { super(entry, true /* node annotations */, edgeAnnotations); this.priorities = priorities; } @Override /** * Returns a node comparator based on the pre-order traversal of the AST. * @param isForward x 'before' y in the pre-order traversal implies * x 'less than' y (if true) and x 'greater than' y (if false). */ public Comparator<DiGraphNode<Node, Branch>> getOptionalNodeComparator( boolean isForward) { if (isForward) { return new Comparator<DiGraphNode<Node, Branch>>() { @Override public int compare( DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) { return getPosition(n1) - getPosition(n2); } }; } else { return new Comparator<DiGraphNode<Node, Branch>>() { @Override public int compare( DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) { return getPosition(n2) - getPosition(n1); } }; } } /** * Gets the pre-order traversal position of the given node. * @return An arbitrary counter used for comparing positions. */ private int getPosition(DiGraphNode<Node, Branch> n) { Integer priority = priorities.get(n); Preconditions.checkNotNull(priority); return priority; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> private final Set<String> forwardDeclaredTypes = new HashSet<String>(); // A map of properties to the types on which those properties have been // declared. private final Map<String, UnionTypeBuilder> typesIndexedByProperty = Maps.newHashMap(); // A map of properties to each reference type on which those // properties have been declared. Each type has a unique name used // for de-duping. private final Map<String, Map<String, ObjectType>> eachRefTypeIndexedByProperty = Maps.newHashMap(); // A map of properties to the greatest subtype on which those properties have // been declared. This is filled lazily from the types declared in // typesIndexedByProperty. private final Map<String, JSType> greatestSubtypeByProperty = Maps.newHashMap(); // A map from interface name to types that implement it. private final Multimap<String, FunctionType> interfaceToImplementors = LinkedHashMultimap.create(); // All the unresolved named types. private final Multimap<StaticScope<JSType>, NamedType> unresolvedNamedTypes = ArrayListMultimap.create(); // All the resolved named types. private final Multimap<StaticScope<JSType>, NamedType> resolvedNamedTypes = ArrayListMultimap.create(); // NamedType warns about unresolved types in the last generation. private boolean lastGeneration = true; // The template type name. private Map<String, TemplateType> templateTypes = Maps.newHashMap(); // A single empty TemplateTypeMap, which can be safely reused in cases where // there are no template types. private final TemplateTypeMap emptyTemplateTypeMap; private final boolean tolerateUndefinedValues; /** * Constructs a new type registry populated with the built-in types. */ public JSTypeRegistry(ErrorReporter reporter) { this(reporter, false); } /** * Constructs a new type registry populated with the built-in types. */ public JSTypeRegistry( ErrorReporter reporter, boolean tolerateUndefinedValues) { this.reporter = reporter; this.emptyTemplateTypeMap = new TemplateTypeMap( this, ImmutableList.<TemplateType>of(), ImmutableList.<JSType>of()); nativeTypes = new JSType[JSTypeNative.values().length]; namesToTypes = new HashMap<String, JSType>(); resetForTypeCheck(); this.tolerateUndefinedValues = tolerateUndefinedValues; } /** * @return The template variable corresponding to the property value type for * Javascript Objects and Arrays. */ public TemplateType getObjectElementKey() { return this.objectElementTemplateKey; } /** * @return The template variable corresponding to the * property key type of the built-in Javascript object. */ public TemplateType getObjectIndexKey() { Preconditions.checkNotNull(objectIndexTemplateKey); return objectIndexTemplateKey; } public ErrorReporter getErrorReporter() { return reporter; } public boolean shouldTolerateUndefinedValues() { return tolerateUndefinedValues; } /** * Reset to run the TypeCheck pass. */ public void resetForTypeCheck() { typesIndexedByProperty.clear(); eachRefTypeIndexedByProperty.clear(); initializeBuiltInTypes(); namesToTypes.clear(); namespaces.clear(); initializeRegistry(); }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>U2U_FUNCTION_TYPE, U2U_FUNCTION_TYPE); // unknown constructor type, i.e. (?...) -> ? with the Unknown type // as instance type FunctionType U2U_CONSTRUCTOR_TYPE = // This is equivalent to // createConstructorType(UNKNOWN_TYPE, true, UNKNOWN_TYPE), but, // in addition, overrides getInstanceType() to return the NoObject type // instead of a new anonymous object. new FunctionType(this, "Function", null, createArrowType( createParametersWithVarArgs(UNKNOWN_TYPE), UNKNOWN_TYPE), UNKNOWN_TYPE, null, true, true) { private static final long serialVersionUID = 1L; @Override public FunctionType getConstructor() { return registry.getNativeFunctionType( JSTypeNative.FUNCTION_FUNCTION_TYPE); } }; // The U2U_CONSTRUCTOR is weird, because it's the supertype of its // own constructor. registerNativeType(JSTypeNative.U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE); registerNativeType( JSTypeNative.FUNCTION_INSTANCE_TYPE, U2U_CONSTRUCTOR_TYPE); FUNCTION_FUNCTION_TYPE.setInstanceType(U2U_CONSTRUCTOR_TYPE); U2U_CONSTRUCTOR_TYPE.setImplicitPrototype(FUNCTION_PROTOTYPE); // least function type, i.e. (All...) -> NoType FunctionType LEAST_FUNCTION_TYPE = createNativeFunctionTypeWithVarArgs(NO_TYPE, ALL_TYPE); registerNativeType(JSTypeNative.LEAST_FUNCTION_TYPE, LEAST_FUNCTION_TYPE); // the 'this' object in the global scope FunctionType GLOBAL_THIS_CTOR = new FunctionType(this, "global this", null, createArrowType(createParameters(false, ALL_TYPE), NUMBER_TYPE), null, null, true, true); ObjectType GLOBAL_THIS = GLOBAL_THIS_CTOR.getInstanceType(); registerNativeType(JSTypeNative.GLOBAL_THIS, GLOBAL_THIS); // greatest function type, i.e. (NoType...) -> All FunctionType GREATEST_FUNCTION_TYPE = createNativeFunctionTypeWithVarArgs(ALL_TYPE, NO_TYPE); registerNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE, GREATEST_FUNCTION_TYPE); // Register the prototype property. See the comments below in // registerPropertyOnType about the bootstrapping process. registerPropertyOnType("prototype", OBJECT_FUNCTION_TYPE); } private void initializeRegistry() { register(getNativeType(JSTypeNative.ARRAY_TYPE)); register(getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE)); register(getNativeType(JSTypeNative.BOOLEAN_TYPE)); register(getNativeType(JSTypeNative.DATE_TYPE)); register(getNativeType(JSTypeNative.NULL_TYPE)); register(getNativeType(JSTypeNative.NULL_TYPE), "Null"); register(getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE)); register(getNativeType(JSTypeNative.NUMBER_TYPE)); register(getNativeType(JSTypeNative.OBJECT_TYPE)); register(getNative

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Type(JSTypeNative.ERROR_TYPE)); register(getNativeType(JSTypeNative.URI_ERROR_TYPE)); register(getNativeType(JSTypeNative.EVAL_ERROR_TYPE)); register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE)); register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE)); register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE)); register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE)); register(getNativeType(JSTypeNative.REGEXP_TYPE)); register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE)); register(getNativeType(JSTypeNative.STRING_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined"); register(getNativeType(JSTypeNative.VOID_TYPE), "void"); register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function"); } private void register(JSType type) { register(type, type.toString()); } private void register(JSType type, String name) { Preconditions.checkArgument( !name.contains("<"), "Type names cannot contain template annotations."); namesToTypes.put(name, type); // Add all the namespaces in which this name lives. while (name.indexOf('.') > 0) { name = name.substring(0, name.lastIndexOf('.')); namespaces.add(name); } } private void registerNativeType(JSTypeNative typeId, JSType type) { nativeTypes[typeId.ordinal()] = type; } /** * Tells the type system that {@code owner} may have a property named * {@code propertyName}. This allows the registry to keep track of what * types a property is defined upon. * * This is NOT the same as saying that {@code owner} must have a property * named type. ObjectType#hasProperty attempts to minimize false positives * ("if we're not sure, then don't type check this property"). The type * registry, on the other hand, should attempt to minimize false negatives * ("if this property is assigned anywhere in the program, it must * show up in the type registry"). */ public void registerPropertyOnType(String propertyName, JSType type) { UnionTypeBuilder typeSet = typesIndexedByProperty.get(propertyName); if (typeSet == null) { typeSet = new UnionTypeBuilder(this, PROPERTY_CHECKING_UNION_SIZE); typesIndexedByProperty.put(propertyName, typeSet); } typeSet.addAlternate(type); addReferenceTypeIndexedByProperty(propertyName, type); // Clear cached values that depend on typesIndexedByProperty. greatestSubtypeByProperty.remove(propertyName); } private void addReferenceTypeIndexedByProperty( String propertyName, JSType type) { if (type instanceof ObjectType && ((ObjectType) type).hasReferenceName()) { Map<String, ObjectType> typeSet = eachRefTypeIndexedByProperty.get(propertyName); if (typeSet == null) { typeSet = Maps.newHashMap(); eachRefTypeIndexedByProperty.put(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> interfaceToImplementors.put(interfaceInstance.getReferenceName(), type); } /** * Returns a collection of types that directly implement {@code * interfaceInstance}. Subtypes of implementing types are not guaranteed to * be returned. {@code interfaceInstance} must be an ObjectType for the * instance of the interface. */ public Collection<FunctionType> getDirectImplementors( ObjectType interfaceInstance) { return interfaceToImplementors.get(interfaceInstance.getReferenceName()); } /** * Records declared global type names. This makes resolution faster * and more robust in the common case. * * @param name The name of the type to be recorded. * @param t The actual type being associated with the name. * @return True if this name is not already defined, false otherwise. */ public boolean declareType(String name, JSType t) { if (namesToTypes.containsKey(name)) { return false; } register(t, name); return true; } /** * Overrides a declared global type name. Throws an exception if this * type name hasn't been declared yet. */ public void overwriteDeclaredType(String name, JSType t) { Preconditions.checkState(namesToTypes.containsKey(name)); register(t, name); } /** * Records a forward-declared type name. We will not emit errors if this * type name never resolves to anything. */ public void forwardDeclareType(String name) { forwardDeclaredTypes.add(name); } /** * Whether this is a forward-declared type name. */ public boolean isForwardDeclaredType(String name) { return forwardDeclaredTypes.contains(name); } /** Determines whether the given JS package exists. */ public boolean hasNamespace(String name) { return namespaces.contains(name); } /** * Looks up a type by name. * * @param jsTypeName The name string. * @return the corresponding JSType object or {@code null} it cannot be found */ public JSType getType(String jsTypeName) { // TODO(user): Push every local type name out of namesToTypes so that // NamedType#resolve is correct. TemplateType templateType = templateTypes.get(jsTypeName); if (templateType != null) { return templateType; } return namesToTypes.get(jsTypeName); } public JSType getNativeType(JSTypeNative typeId) { return nativeTypes[typeId.ordinal()]; } public ObjectType getNativeObjectType(JSTypeNative typeId) { return (ObjectType) getNativeType(typeId); } public FunctionType getNativeFunctionType(JSTypeNative typeId) { return (FunctionType) getNativeType(typeId); } /** * Looks up a type by name. To allow for forward references to types, an * unrecognized string has to be bound to a NamedType object that will be * resolved later. * * @param scope A scope for doing type name resolution. * @param jsTypeName The name string. * @param sourceName The name of the source file where this reference appears. * @param lineno The line number of the reference. * @return a NamedType if the string argument

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> some reason. There are * a few different reasons why this could fail: for example, numbers * can't be implicit prototypes, and we don't want to change the implicit * prototype if other classes have already subclassed this one. */ public boolean resetImplicitPrototype( JSType type, ObjectType newImplicitProto) { if (type instanceof PrototypeObjectType) { PrototypeObjectType poType = (PrototypeObjectType) type; poType.clearCachedValues(); poType.setImplicitPrototype(newImplicitProto); return true; } return false; } /** * Create an anonymous object type for a native type. */ ObjectType createNativeAnonymousObjectType() { PrototypeObjectType type = new PrototypeObjectType(this, null, null, true, null); type.setPrettyPrint(true); return type; } /** * Creates a constructor function type. * @param name the function's name or {@code null} to indicate that the * function is anonymous. * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. * @param parameters the function's parameters or {@code null} * to indicate that the parameter types are unknown. * @param returnType the function's return type or {@code null} to indicate * that the return type is unknown. * @param templateKeys the templatized types for the class. */ public FunctionType createConstructorType(String name, Node source, Node parameters, JSType returnType, ImmutableList<TemplateType> templateKeys) { return new FunctionType(this, name, source, createArrowType(parameters, returnType), null, createTemplateTypeMap(templateKeys, null), true, false); } ImmutableList<TemplateType> createTemplateMapKeys(ImmutableList<String> keys) { ImmutableList.Builder<TemplateType> builder = ImmutableList.builder(); if (keys != null) { for (String key : keys) { builder.add(new TemplateType(this, key)); } } return builder.build(); } /** * Creates an interface function type. * @param name the function's name * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. * @param templateKeys the templatized types for the interface. */ public FunctionType createInterfaceType(String name, Node source, ImmutableList<TemplateType> templateKeys) { return FunctionType.forInterface(this, name, source, createTemplateTypeMap(templateKeys, null)); } public TemplateType createTemplateType(String name) { return new TemplateType(this, name); } /** * Creates a template type map from the specified list of template keys and * template value types. */ public TemplateTypeMap createTemplateTypeMap( ImmutableList<TemplateType> templateKeys, ImmutableList<JSType> templateValues) { templateKeys = templateKeys == null ? ImmutableList.<TemplateType>of() : templateKeys; templateValues = templateValues == null ? ImmutableList.<JSType>of() : templateValues; return (templateKeys.isEmpty() && templateValues.isEmpty()) ? emptyTemplateTypeMap :

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> new TemplateTypeMap(this, templateKeys, templateValues); } /** * Creates a templatized instance of the specified type. Only ObjectTypes * can currently be templatized; extend the logic in this function when * more types can be templatized. * @param baseType the type to be templatized. * @param templatizedTypes a list of the template JSTypes. Will be matched by * list order to the template keys on the base type. */ public TemplatizedType createTemplatizedType( ObjectType baseType, ImmutableList<JSType> templatizedTypes) { // Only ObjectTypes can currently be templatized; extend this logic when // more types can be templatized. return new TemplatizedType(this, baseType, templatizedTypes); } /** * Creates a templatized instance of the specified type. Only ObjectTypes * can currently be templatized; extend the logic in this function when * more types can be templatized. * @param baseType the type to be templatized. * @param templatizedTypes a map from TemplateType to corresponding JSType * value. Any unfilled TemplateTypes on the baseType that are *not* * contained in this map will have UNKNOWN_TYPE used as their value. */ public TemplatizedType createTemplatizedType( ObjectType baseType, Map<TemplateType, JSType> templatizedTypes) { ImmutableList.Builder<JSType> builder = ImmutableList.builder(); TemplateTypeMap baseTemplateTypeMap = baseType.getTemplateTypeMap(); for (TemplateType key : baseTemplateTypeMap.getUnfilledTemplateKeys()) { JSType templatizedType = templatizedTypes.containsKey(key) ? templatizedTypes.get(key) : getNativeType(UNKNOWN_TYPE); builder.add(templatizedType); } return createTemplatizedType(baseType, builder.build()); } /** * Creates a templatized instance of the specified type. Only ObjectTypes * can currently be templatized; extend the logic in this function when * more types can be templatized. * @param baseType the type to be templatized. * @param templatizedTypes a list of the template JSTypes. Will be matched by * list order to the template keys on the base type. */ public TemplatizedType createTemplatizedType( ObjectType baseType, JSType... templatizedTypes) { return createTemplatizedType( baseType, ImmutableList.copyOf(templatizedTypes)); } /** * Creates a named type. */ @VisibleForTesting public NamedType createNamedType(String reference, String sourceName, int lineno, int charno) { if (reference.endsWith(".")) { return new NamespaceType(this, reference, sourceName, lineno, charno); } else { return new NamedType(this, reference, sourceName, lineno, charno); } } /** * Identifies the name of a typedef or enum before we actually declare it. */ public void identifyNonNullableName(String name) { Preconditions.checkNotNull(name); nonNullableTypeNames.add(name);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) { return createFromTypeNodesInternal(n, sourceName, scope); } private boolean hasTypeName(Node n) { if (n.getType() == Token.STRING) { return true; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (hasTypeName(child)) { return true; } } return false; } /** @see #createFromTypeNodes(Node, String, StaticScope) */ private JSType createFromTypeNodesInternal(Node n, String sourceName, StaticScope<JSType> scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes( n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable or unknown Node firstChild = n.getFirstChild(); if (firstChild == null) { return getNativeType(UNKNOWN_TYPE); } return createDefaultObjectUnion( createFromTypeNodesInternal( firstChild, sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if ((namedType instanceof ObjectType) && !(namedType instanceof NamespaceType) && !(nonNullableTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); int nAllowedTypes = namedType.getTemplateTypeMap().numUnfilledTemplateKeys(); if (typeList != null && nAllowedTypes > 0) { // Templatized types.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>"), sourceName, arg.getLineno(), arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope); return new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeNames(List<TemplateType> keys) { Preconditions.checkNotNull(keys); for (TemplateType key : keys) { templateTypes.put(key.getReferenceName(), key); } } /** * Clears the template type name. */ public void clearTemplateTypeNames() { templateTypes.clear(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> null (in particular, if not overridden) */ public Comparator<DiGraphNode<N, Branch>> getOptionalNodeComparator( boolean isForward) { return null; } /** * The edge object for the control flow graph. */ public static enum Branch { /** Edge is taken if the condition is true. */ ON_TRUE, /** Edge is taken if the condition is false. */ ON_FALSE, /** Unconditional branch. */ UNCOND, /** * Exception-handling code paths. * Conflates two kind of control flow passing: * - An exception is thrown, and falls into a catch or finally block * - During exception handling, a finally block finishes and control * passes to the next finally block. * In theory, we need 2 different edge types. In practice, we * can just treat them as "the edges we can't really optimize". */ ON_EX, /** Possible folded-away template */ SYN_BLOCK; public boolean isConditional() { return this == ON_TRUE || this == ON_FALSE; } } /** * Abstract callback to visit a control flow graph node without going into * subtrees of the node that are also represented by other * control flow graph nodes. * * <p>For example, traversing an IF node as root will visit the two subtrees * pointed by the {@link ControlFlowGraph.Branch#ON_TRUE} and * {@link ControlFlowGraph.Branch#ON_FALSE} edges. */ public abstract static class AbstractCfgNodeTraversalCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { if (parent == null) { return true; } return !isEnteringNewCfgNode(n); } } /** * @return True if n should be represented by a new CFG node in the control * flow graph. */ public static boolean isEnteringNewCfgNode(Node n) { Node parent = n.getParent(); switch (parent.getType()) { case Token.BLOCK: case Token.SCRIPT: case Token.TRY: return true; case Token.FUNCTION: // A function node represents the start of a function where the name // bleeds into the local scope and parameters are assigned // to the formal argument names. The node includes the name of the // function and the LP list since we assume the whole set up process // is atomic without change in control flow. The next change of // control is going into the function's body, represented by the second // child. return n != parent.getFirstChild().getNext(); case Token.WHILE: case Token.DO: case Token.IF: // These control structures are represented by a node that holds the // condition. Each of them is a branch node based on its condition. return NodeUtil.getConditionExpression(parent) != n; case Token.FOR: // The FOR(;;) node differs from other control structures in that // it has an initialization and an increment statement. Those // two statements have corresponding CFG nodes to represent them. // The FOR node only represents the condition check for each iteration. // That way the following: // for(var

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> x = 0; x < 10; x++) { } has a graph that is isomorphic to // var x = 0; while(x<10) { x++; } if (NodeUtil.isForIn(parent)) { // TODO(user): Investigate how we should handle the case where // we have a very complex expression inside the FOR-IN header. return n != parent.getFirstChild(); } else { return NodeUtil.getConditionExpression(parent) != n; } case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.WITH: return n != parent.getFirstChild(); default: return false; } } @Override public String toString() { String s = "CFG:\n"; for (GraphvizEdge e : getGraphvizEdges()) { s += e.toString() + '\n'; } return s; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>CAST_TO_VISITOR = new CanCastToVisitor(); public static final String UNKNOWN_NAME = "Unknown class name"; public static final String NOT_A_CLASS = "Not declared as a constructor"; public static final String NOT_A_TYPE = "Not declared as a type name"; public static final String EMPTY_TYPE_COMPONENT = "Named type with empty name component"; /** * Total ordering on types based on their textual representation. * This is used to have a deterministic output of the toString * method of the union type since this output is used in tests. */ static final Comparator<JSType> ALPHA = new Comparator<JSType>() { @Override public int compare(JSType t1, JSType t2) { return t1.toString().compareTo(t2.toString()); } }; // A flag set on enum definition tree nodes public static final int ENUMDECL = 1; public static final int NOT_ENUMDECL = 0; final JSTypeRegistry registry; JSType(JSTypeRegistry registry) { this(registry, null); } JSType(JSTypeRegistry registry, TemplateTypeMap templateTypeMap) { this.registry = registry; this.templateTypeMap = templateTypeMap == null ? registry.createTemplateTypeMap(null, null) : templateTypeMap; } /** * Utility method for less verbose code. */ JSType getNativeType(JSTypeNative typeId) { return registry.getNativeType(typeId); } /** * Gets the docInfo for this type. By default, documentation cannot be * attached to arbitrary types. This must be overridden for * programmer-defined types. */ public JSDocInfo getJSDocInfo() { return null; } /** * Returns a user meaningful label for the JSType instance. For example, * Functions and Enums will return their declaration name (if they have one). * Some types will not have a meaningful display name. Calls to * hasDisplayName() will return true IFF getDisplayName() will return null * or a zero length string. * * @return the display name of the type, or null if one is not available */ public String getDisplayName() { return null; } /** * @return true if the JSType has a user meaningful label. */ public boolean hasDisplayName() { String displayName = getDisplayName(); return displayName != null && !displayName.isEmpty(); } /** * Checks whether the property is present on the object. * @param pname The property name. */ public boolean hasProperty(String pname) { return false; } public boolean isNoType() { return false; } public boolean isNoResolvedType() { return false; } public boolean isNoObjectType() { return false; } public final boolean isEmptyType() { return isNoType() || isNoObjectType() || isNoResolvedType() || (registry.getNativeFunctionType( JSTypeNative.LEAST_FUNCTION_TYPE) == this); } public boolean isNumberObjectType() { return false; } public boolean isNumberValueType() { return false; } /** Whether this is the prototype of a function. */

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } public static boolean isEquivalent(JSType typeA, JSType typeB) { return (typeA == null || typeB == null) ? typeA == typeB : typeA.isEquivalentTo(typeB); } @Override public boolean equals(Object jsType) { return (jsType instanceof JSType) ? isEquivalentTo((JSType) jsType) : false; } @Override public int hashCode() { return System.identityHashCode(this); } /** * This predicate is used to test whether a given type can appear in a * 'Int32' context. This context includes, for example, the operands of a * bitwise or operator. Since we do not currently support integer types, * this is a synonym for {@code Number}. */ public final boolean matchesInt32Context() { return matchesNumberContext(); } /** * This predicate is used to test whether a given type can appear in a * 'Uint32' context. This context includes the right-hand operand of a shift * operator. */ public final boolean matchesUint32Context() { return matchesNumberContext(); } /** * This predicate is used to test whether a given type can appear in a * numeric context, such as an operand of a multiply operator. */ public boolean matchesNumberContext() { return false; } /** * This predicate is used to test whether a given type can appear in a * {@code String} context, such as an operand of a string concat (+) operator. * * All types have at least the potential for converting to {@code String}. * When we add externally defined types, such as a browser OM, we may choose * to add types that do not automatically convert to {@code String}. */ public boolean matchesStringContext() { return false; } /** * This predicate is used to test whether a given type can appear in an * {@code Object} context, such as the expression in a with statement. * * Most types we will encounter, except notably {@code null}, have at least * the potential for converting to {@code Object}. Host defined objects can * get peculiar. */ public boolean matchesObjectContext() { return false; } /** * Coerces this type to an Object type, then gets the type of the property * whose name is given. * * Unlike {@link ObjectType#getPropertyType}, returns null if the property * is not found. * * @return The property's type. {@code null} if the current type cannot * have properties, or if the type is not found. */ public JSType findPropertyType(String propertyName) { ObjectType autoboxObjType = ObjectType.cast(autoboxesTo()); if (autoboxObjType != null) { return autoboxObjType.findPropertyType(propertyName); } return null; } /** * This predicate is used to test whether a given type can be used as the * 'function' in a function call. * * @return {@code true} if this type might be callable. */ public boolean canBeCalled() { return false; } /** * Tests whether values of {@code

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Any Type if the underlying type could * not have yielded this ToBoolean value * * TODO(user): Move this method to the SemanticRAI and use the visit * method of types to get the restricted type. */ public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) { return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); } BooleanLiteralSet literals = getPossibleToBooleanOutcomes(); if (literals.contains(outcome)) { return this; } else { return getNativeType(JSTypeNative.NO_TYPE); } } /** * Computes the set of possible outcomes of the {@code ToBoolean} predicate * for this type. The {@code ToBoolean} predicate is defined by the ECMA-262 * standard, 3<sup>rd</sup> edition. Its behavior for simple types can be * summarized by the following table: * <table> * <tr><th>type</th><th>result</th></tr> * <tr><td>{@code undefined}</td><td>{false}</td></tr> * <tr><td>{@code null}</td><td>{false}</td></tr> * <tr><td>{@code boolean}</td><td>{true, false}</td></tr> * <tr><td>{@code number}</td><td>{true, false}</td></tr> * <tr><td>{@code string}</td><td>{true, false}</td></tr> * <tr><td>{@code Object}</td><td>{true}</td></tr> * </table> * @return the set of boolean literals for this type */ public abstract BooleanLiteralSet getPossibleToBooleanOutcomes(); /** * Computes the subset of {@code this} and {@code that} types if equality * is observed. If a value {@code v1} of type {@code null} is equal to a value * {@code v2} of type {@code (undefined,number)}, we can infer that the * type of {@code v1} is {@code null} and the type of {@code v2} is * {@code undefined}. * * @return a pair containing the restricted type of {@code this} as the first * component and the restricted type of {@code that} as the second * element. The returned pair is never {@code null} even though its * components may be {@code null} */ public TypePair getTypesUnderEquality(JSType that) { // unions types if (that.isUnionType()) { TypePair p = that.toMaybeUnionType().getTypesUnderEquality(this); return new TypePair(p.typeB, p.typeA); } // other types switch (testForEquality(that)) { case FALSE: return new TypePair(null, null); case TRUE: case UNKNOWN: return new TypePair(this, that); } // switch case is exhaustive throw new IllegalStateException(); } /** * Computes the subset of {@code this} and {@code that} types if inequality * is observed. If a value {@code v

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> // values are subtypes/supertypes of one another. TemplateType key = thisType.registry.getObjectElementKey(); JSType thisElement = thisTypeParams.getTemplateType(key); JSType thatElement = thatTypeParams.getTemplateType(key); templateMatch = thisElement.isSubtype(thatElement) || thatElement.isSubtype(thisElement); } else { templateMatch = thisTypeParams.checkEquivalenceHelper( thatTypeParams, EquivalenceMethod.INVARIANT); } if (!templateMatch) { return false; } // Templatized types. The above check guarantees TemplateTypeMap // equivalence; check if the base type is a subtype. if (thisType.isTemplatizedType()) { return thisType.toMaybeTemplatizedType().getReferencedType().isSubtype( thatType); } // proxy types if (thatType instanceof ProxyObjectType) { return thisType.isSubtype( ((ProxyObjectType) thatType).getReferencedTypeInternal()); } return false; } /** * Determines if the specified type is exempt from standard invariant * templatized typing rules. */ static boolean isExemptFromTemplateTypeInvariance(JSType type) { ObjectType objType = type.toObjectType(); return objType == null || "Array".equals(objType.getReferenceName()) || "Object".equals(objType.getReferenceName()); } /** * Visit this type with the given visitor. * @see com.google.javascript.rhino.jstype.Visitor * @return the value returned by the visitor */ public abstract <T> T visit(Visitor<T> visitor); /** * Visit the types with the given visitor. * @see com.google.javascript.rhino.jstype.RelationshipVisitor * @return the value returned by the visitor */ abstract <T> T visit(RelationshipVisitor<T> visitor, JSType that); /** * Resolve this type in the given scope. * * The returned value must be equal to {@code this}, as defined by * {@link #isEquivalentTo}. It may or may not be the same object. This method * may modify the internal state of {@code this}, as long as it does * so in a way that preserves Object equality. * * For efficiency, we should only resolve a type once per compilation job. * For incremental compilations, one compilation job may need the * artifacts from a previous generation, so we will eventually need * a generational flag instead of a boolean one. */ public final JSType resolve(ErrorReporter t, StaticScope<JSType> scope) { if (resolved) { // TODO(nicksantos): Check to see if resolve() looped back on itself. // Preconditions.checkNotNull(resolveResult); if (resolveResult == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return resolveResult; } resolved = true; resolveResult = resolveInternal(t, scope); resolveResult.setResolvedTypeInternal(resolveResult); return resolveResult; } /** * @see #resolve */ abstract JSType resolveInternal(ErrorReporter t, StaticScope<JSType>

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> scope); void setResolvedTypeInternal(JSType type) { resolveResult = type; resolved = true; } /** Whether the type has been resolved. */ public final boolean isResolved() { return resolved; } /** Clears the resolved field. */ public final void clearResolved() { resolved = false; resolveResult = null; } /** * A null-safe resolve. * @see #resolve */ static final JSType safeResolve( JSType type, ErrorReporter t, StaticScope<JSType> scope) { return type == null ? null : type.resolve(t, scope); } /** * Certain types have constraints on them at resolution-time. * For example, a type in an {@code @extends} annotation must be an * object. Clients should inject a validator that emits a warning * if the type does not validate, and return false. */ public boolean setValidator(Predicate<JSType> validator) { return validator.apply(this); } public static class TypePair { public final JSType typeA; public final JSType typeB; public TypePair(JSType typeA, JSType typeB) { this.typeA = typeA; this.typeB = typeB; } } /** * A string representation of this type, suitable for printing * in warnings. */ @Override public String toString() { return toStringHelper(false); } /** * A hash code function for diagnosing complicated issues * around type-identity. */ public String toDebugHashCodeString() { return "{" + hashCode() + "}"; } /** * A string representation of this type, suitable for printing * in type annotations at code generation time. */ public final String toAnnotationString() { return toStringHelper(true); } /** * @param forAnnotations Whether this is for use in code generator * annotations. Otherwise, it's for warnings. */ abstract String toStringHelper(boolean forAnnotations); /** * Modify this type so that it matches the specified type. * * This is useful for reverse type-inference, where we want to * infer that an object literal matches its constraint (much like * how the java compiler does reverse-inference to figure out generics). * @param constraint */ public void matchConstraint(JSType constraint) {} }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; /** * The arrow type is an internal type that models the functional arrow type * seen in typical functional programming languages. It is used solely for * separating the management of the arrow type from the complex * {@link FunctionType} that models JavaScript's notion of functions. */ final class ArrowType extends JSType { private static final long serialVersionUID = 1L; final Node parameters; JSType returnType; // Whether the return type is inferred. final boolean returnTypeInferred; ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType) { this(registry, parameters, returnType, false); } ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType, boolean returnTypeInferred) { super(registry); this.parameters = parameters == null ? registry.createParametersWithVarArgs(getNativeType(UNKNOWN_TYPE)) : parameters; this.returnType = returnType == null ? getNativeType(UNKNOWN_TYPE) : returnType; this.returnTypeInferred = returnTypeInferred; } @Override public boolean isSubtype(JSType other) { if (!(other instanceof ArrowType)) { return false;

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> null; thatParam = null; } } // "that" can't be a supertype, because it's missing a required argument. if (thisParam != null && !thisParam.isOptionalArg() && !thisParam.isVarArgs() && thatParam == null) { return false; } return true; } /** * @return True if our parameter spec is equal to {@code that}'s parameter * spec. */ boolean hasEqualParameters(ArrowType that, EquivalenceMethod eqMethod) { Node thisParam = parameters.getFirstChild(); Node otherParam = that.parameters.getFirstChild(); while (thisParam != null && otherParam != null) { JSType thisParamType = thisParam.getJSType(); JSType otherParamType = otherParam.getJSType(); if (thisParamType != null) { // Both parameter lists give a type for this param, it should be equal if (otherParamType != null && !thisParamType.checkEquivalenceHelper( otherParamType, eqMethod)) { return false; } } else { if (otherParamType != null) { return false; } } // Check var_args/optionality if (thisParam.isOptionalArg() != otherParam.isOptionalArg()) { return false; } if (thisParam.isVarArgs() != otherParam.isVarArgs()) { return false; } thisParam = thisParam.getNext(); otherParam = otherParam.getNext(); } // One of the parameters is null, so the types are only equal if both // parameter lists are null (they are equal). return thisParam == otherParam; } boolean checkArrowEquivalenceHelper( ArrowType that, EquivalenceMethod eqMethod) { // Please keep this method in sync with the hashCode() method below. if (!returnType.checkEquivalenceHelper(that.returnType, eqMethod)) { return false; } return hasEqualParameters(that, eqMethod); } @Override public int hashCode() { int hashCode = 0; if (returnType != null) { hashCode += returnType.hashCode(); } if (returnTypeInferred) { hashCode += 1; } if (parameters != null) { Node param = parameters.getFirstChild(); while (param != null) { JSType paramType = param.getJSType(); if (paramType != null) { hashCode += paramType.hashCode(); } param = param.getNext(); } } return hashCode; } @Override public JSType getLeastSupertype(JSType that) { throw new UnsupportedOperationException(); } @Override public JSType getGreatestSubtype(JSType that) { throw new UnsupportedOperationException(); } @Override public TernaryValue testForEquality(JSType that) { throw new UnsupportedOperationException(); } @Override public <T> T visit(Visitor<T> visitor) { throw new UnsupportedOperationException(); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { throw new UnsupportedOperationException(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.TRUE

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { returnType = safeResolve(returnType, t, scope); if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { paramNode.setJSType(paramNode.getJSType().resolve(t, scope)); } } return this; } boolean hasUnknownParamsOrReturn() { if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { JSType type = paramNode.getJSType(); if (type == null || type.isUnknownType()) { return true; } } } return returnType == null || returnType.isUnknownType(); } @Override String toStringHelper(boolean forAnnotations) { return "[ArrowType]"; } @Override public boolean hasAnyTemplateTypesInternal() { return returnType.hasAnyTemplateTypes() || hasTemplatedParameterType(); } private boolean hasTemplatedParameterType() { if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { JSType type = paramNode.getJSType(); if (type != null && type.hasAnyTemplateTypes()) { return true; } } } return false; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> that the {@link Visibility#compareTo} method can be used to * determine if a visibility is more permissive than another. */ public enum Visibility { PRIVATE, PROTECTED, PUBLIC, // If visibility is not specified, we just assume that visibility // is inherited from the super class. INHERITED } private static final class LazilyInitializedInfo implements Serializable { private static final long serialVersionUID = 1L; // Function information JSTypeExpression baseType = null; List<JSTypeExpression> extendedInterfaces = null; List<JSTypeExpression> implementedInterfaces = null; Map<String, JSTypeExpression> parameters = null; List<JSTypeExpression> thrownTypes = null; ImmutableList<String> templateTypeNames = null; Set<String> disposedParameters = null; // Other information String description = null; String meaning = null; String deprecated = null; String license = null; Set<String> suppressions = null; Set<String> modifies = null; String lendsName = null; // TODO(nnaze): Consider converting the boolean flags to bit fields. boolean ngInject = false; boolean wizaction = false; // Tags for Jagger dependency injection prototype boolean jaggerInject = false; boolean jaggerProvide = false; boolean jaggerModule = false; } private static final class LazilyInitializedDocumentation { String sourceComment = null; List<Marker> markers = null; Map<String, String> parameters = null; Map<JSTypeExpression, String> throwsDescriptions = null; String blockDescription = null; String fileOverview = null; String returnDescription = null; String version = null; List<String> authors = null; List<String> sees = null; } /** * A piece of information (found in a marker) which contains a position * with a string. */ public static class StringPosition extends SourcePosition<String> { } /** * A piece of information (found in a marker) which contains a position * with a string that has no leading or trailing whitespace. */ static class TrimmedStringPosition extends StringPosition { @Override public void setItem(String item) { Preconditions.checkArgument( item.charAt(0) != ' ' && item.charAt(item.length() - 1) != ' ', "String has leading or trailing whitespace"); super.setItem(item); } } /** * A piece of information (found in a marker) which contains a position * with a name node. */ public static class NamePosition extends SourcePosition<Node> {} /** * A piece of information (found in a marker) which contains a position * with a type expression syntax tree. */ public static class TypePosition extends SourcePosition<Node> { private boolean brackets = false; /** Returns whether the type has curly braces around it. */ public boolean hasBrackets() { return brackets; } void setHasBrackets(boolean newVal) { brackets = newVal; } } /** * Defines a class for containing the parsing information * for this JSDocInfo. For each annotation found in the * JsDoc, a marker will be created indicating

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> void setJaggerProvide(boolean jaggerProvide) { lazyInitInfo(); info.jaggerProvide = jaggerProvide; } /** * Returns whether JSDoc is annotated with {@code @jaggerModule} annotation. */ public boolean isJaggerModule() { return (info != null) && info.jaggerModule; } void setJaggerModule(boolean jaggerModule) { lazyInitInfo(); info.jaggerModule = jaggerModule; } /** * Returns whether JSDoc is annotated with {@code @wizaction} annotation. */ public boolean isWizaction() { return (info == null) ? false : info.wizaction; } void setWizaction(boolean wizaction) { lazyInitInfo(); info.wizaction = wizaction; } /** * Returns whether JSDoc is annotated with {@code @disposes} annotation. */ public boolean isDisposes() { return (info == null) ? false : info.disposedParameters != null; } boolean setDisposedParameter(String parameterName) { lazyInitInfo(); // Lazily initialize disposedParameters if (info.disposedParameters == null) { info.disposedParameters = Sets.newHashSet(); } if (info.disposedParameters.contains(parameterName)) { return false; } else { info.disposedParameters.add(parameterName); return true; } } /** * Return whether the function disposes of specified parameter. */ public boolean disposesOf(String parameterName) { return isDisposes() && info.disposedParameters.contains(parameterName); } /** * Gets the description specified by the {@code @license} annotation. */ public String getLicense() { return (info == null) ? null : info.license; } /** License directives can appear in multiple comments, and always * apply to the entire file. Break protection and allow outsiders to * update the license string so that we can attach the license text even * when the JSDocInfo has been created and tagged with other information. * @param license String containing new license text. */ public void setLicense(String license) { lazyInitInfo(); info.license = license; } @Override public String toString() { return "JSDocInfo"; } /** * Returns whether this {@link JSDocInfo} contains a type for {@code @extends} * annotation. */ public boolean hasBaseType() { return getBaseType() != null; } /** * Adds an implemented interface. Returns whether the interface was added. If * the interface was already present in the list, it won't get added again. */ boolean addImplementedInterface(JSTypeExpression interfaceName) { lazyInitInfo(); if (info.implementedInterfaces == null) { info.implementedInterfaces = Lists.newArrayListWithCapacity(2); } if (info.implementedInterfaces.contains(interfaceName)) { return false; } info.implementedInterfaces.add(interfaceName); return true; } /** * Returns the types specified by the {@code @implements} annotation. * * @return An immutable

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> list of JSTypeExpression objects that can * be resolved to types. */ public List<JSTypeExpression> getImplementedInterfaces() { if (info == null || info.implementedInterfaces == null) { return ImmutableList.of(); } return Collections.unmodifiableList(info.implementedInterfaces); } /** * Gets the number of interfaces specified by the {@code @implements} * annotation. */ public int getImplementedInterfaceCount() { if (info == null || info.implementedInterfaces == null) { return 0; } return info.implementedInterfaces.size(); } /** * Adds an extended interface (for interface only). * Returns whether the type was added. * if the type was already present in the list, it won't get added again. */ boolean addExtendedInterface(JSTypeExpression type) { lazyInitInfo(); if (info.extendedInterfaces == null) { info.extendedInterfaces = Lists.newArrayListWithCapacity(2); } if (info.extendedInterfaces.contains(type)) { return false; } info.extendedInterfaces.add(type); return true; } /** * Returns the interfaces extended by an interface * * @return An immutable list of JSTypeExpression objects that can * be resolved to types. */ public List<JSTypeExpression> getExtendedInterfaces() { if (info == null || info.extendedInterfaces == null) { return ImmutableList.of(); } return Collections.unmodifiableList(info.extendedInterfaces); } /** * Gets the number of extended interfaces specified */ public int getExtendedInterfacesCount() { if (info == null || info.extendedInterfaces == null) { return 0; } return info.extendedInterfaces.size(); } /** * Returns the deprecation reason or null if none specified. */ public String getDeprecationReason() { return info == null ? null : info.deprecated; } /** * Returns the set of suppressed warnings. */ public Set<String> getSuppressions() { Set<String> suppressions = info == null ? null : info.suppressions; return suppressions == null ? Collections.<String>emptySet() : suppressions; } /** * Returns the set of sideeffect notations. */ public Set<String> getModifies() { Set<String> modifies = info == null ? null : info.modifies; return modifies == null ? Collections.<String>emptySet() : modifies; } /** * Returns whether a description exists for the parameter with the specified * name. */ public boolean hasDescriptionForParameter(String name) { if (documentation == null || documentation.parameters == null) { return false; } return documentation.parameters.containsKey(name); } /** * Returns the description for the parameter with the given name, if its * exists. */ public String getDescriptionForParameter(String name) { if (documentation == null || documentation.parameters == null) { return null; } return documentation.parameters.get(name); } /** * Returns the list of authors or null if none. */ public Collection<String> getAuthors() { return documentation == null ?

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>TYPE; protected ObjectType DATE_TYPE; protected JSType ERROR_FUNCTION_TYPE; protected ObjectType ERROR_TYPE; protected JSType EVAL_ERROR_FUNCTION_TYPE; protected ObjectType EVAL_ERROR_TYPE; protected FunctionType FUNCTION_FUNCTION_TYPE; protected FunctionType FUNCTION_INSTANCE_TYPE; protected ObjectType FUNCTION_PROTOTYPE; protected JSType GREATEST_FUNCTION_TYPE; protected JSType LEAST_FUNCTION_TYPE; protected JSType MATH_TYPE; protected JSType NULL_TYPE; protected JSType NUMBER_OBJECT_FUNCTION_TYPE; protected ObjectType NUMBER_OBJECT_TYPE; protected JSType NUMBER_STRING_BOOLEAN; protected JSType NUMBER_TYPE; protected FunctionType OBJECT_FUNCTION_TYPE; protected JSType NULL_VOID; protected JSType OBJECT_NUMBER_STRING; protected JSType OBJECT_NUMBER_STRING_BOOLEAN; protected JSType OBJECT_PROTOTYPE; protected ObjectType OBJECT_TYPE; protected JSType RANGE_ERROR_FUNCTION_TYPE; protected ObjectType RANGE_ERROR_TYPE; protected JSType REFERENCE_ERROR_FUNCTION_TYPE; protected ObjectType REFERENCE_ERROR_TYPE; protected JSType REGEXP_FUNCTION_TYPE; protected ObjectType REGEXP_TYPE; protected JSType STRING_OBJECT_FUNCTION_TYPE; protected ObjectType STRING_OBJECT_TYPE; protected JSType STRING_TYPE; protected JSType SYNTAX_ERROR_FUNCTION_TYPE; protected ObjectType SYNTAX_ERROR_TYPE; protected JSType TYPE_ERROR_FUNCTION_TYPE; protected ObjectType TYPE_ERROR_TYPE; protected FunctionType U2U_CONSTRUCTOR_TYPE; protected FunctionType U2U_FUNCTION_TYPE; protected ObjectType UNKNOWN_TYPE; protected JSType URI_ERROR_FUNCTION_TYPE; protected ObjectType URI_ERROR_TYPE; protected JSType VOID_TYPE; protected int NATIVE_PROPERTIES_COUNT; @Override protected void setUp() throws Exception { super.setUp(); errorReporter = new TestErrorReporter(null, null); registry = new JSTypeRegistry(errorReporter); initTypes(); } protected void initTypes() { ALL_TYPE = registry.getNativeType(JSTypeNative.ALL_TYPE); NO_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE); NO_TYPE = registry.getNativeObjectType(JSTypeNative.NO_TYPE); NO_RESOLVED_TYPE = registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE); ARRAY_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE); ARRAY_TYPE = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); BOOLEAN_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE); BOOLEAN_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); BOOLEAN_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); CHECKED_UNKNOWN_TYPE = registry.getNativeObjectType(JSTypeNative.CHECKED_UNKNOWN_TYPE); DATE_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.DATE

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>" + "/** @constructor \n * @param {*} var_args */ " + "function Function(var_args) {}" + "/** @type {!Function} */ Function.prototype.apply;" + "/** @type {!Function} */ Function.prototype.bind;" + "/** @type {!Function} */ Function.prototype.call;" + "/** @constructor \n * @param {*=} arg \n @return {string} */" + "function String(arg) {}" + "/** @param {number} sliceArg */\n" + "String.prototype.slice = function(sliceArg) {};" + "/** @type {number} */ String.prototype.length;" + "/** @constructor \n * @param {*} var_args \n @return {!Array} */" + "function Array(var_args) {}\n" + "/** @type {number} */ Array.prototype.length;\n" + "/**\n" + " * @param {...T} var_args\n" + " * @return {number} The new length of the array.\n" + " * @this {{length: number}|Array.<T>}\n" + " * @template T\n" + " * @modifies {this}\n" + " */\n" + "Array.prototype.push = function(var_args) {};" + "/** @constructor */\n" + "function Arguments() {}\n" + "/** @type {number} */\n" + "Arguments.prototype.length;\n" + "/** @type {!Arguments} */\n" + "var arguments;" + "" + ACTIVE_X_OBJECT_DEF; protected Compiler compiler; protected CompilerOptions getOptions() { CompilerOptions options = new CompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT5); options.setWarningLevel( DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.MISPLACED_TYPE_ANNOTATION, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.INVALID_CASTS, CheckLevel.WARNING); options.setCodingConvention(getCodingConvention()); return options; } protected CodingConvention getCodingConvention() { return new GoogleCodingConvention(); } @Override protected void setUp() throws Exception { compiler = new Compiler(); compiler.initOptions(getOptions()); registry = compiler.getTypeRegistry(); initTypes(); } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>). * * When building scope information, also declares relevant information * about types in the type registry. * * @author nicksantos@google.com (Nick Santos) */ final class TypedScopeCreator implements ScopeCreator { /** * A suffix for naming delegate proxies differently from their base. */ static final String DELEGATE_PROXY_SUFFIX = ObjectType.createDelegateSuffix("Proxy"); static final DiagnosticType MALFORMED_TYPEDEF = DiagnosticType.warning( "JSC_MALFORMED_TYPEDEF", "Typedef for {0} does not have any type information"); static final DiagnosticType ENUM_INITIALIZER = DiagnosticType.warning( "JSC_ENUM_INITIALIZER_NOT_ENUM", "enum initializer must be an object literal or an enum"); static final DiagnosticType CTOR_INITIALIZER = DiagnosticType.warning( "JSC_CTOR_INITIALIZER_NOT_CTOR", "Constructor {0} must be initialized at declaration"); static final DiagnosticType IFACE_INITIALIZER = DiagnosticType.warning( "JSC_IFACE_INITIALIZER_NOT_IFACE", "Interface {0} must be initialized at declaration"); static final DiagnosticType CONSTRUCTOR_EXPECTED = DiagnosticType.warning( "JSC_REFLECT_CONSTRUCTOR_EXPECTED", "Constructor expected as first argument"); static final DiagnosticType UNKNOWN_LENDS = DiagnosticType.warning( "JSC_UNKNOWN_LENDS", "Variable {0} not declared before @lends annotation."); static final DiagnosticType LENDS_ON_NON_OBJECT = DiagnosticType.warning( "JSC_LENDS_ON_NON_OBJECT", "May only lend properties to object types. {0} has type {1}."); private final AbstractCompiler compiler; private final ErrorReporter typeParsingErrorReporter; private final TypeValidator validator; private final CodingConvention codingConvention; private final JSTypeRegistry typeRegistry; private final List<ObjectType> delegateProxyPrototypes = Lists.newArrayList(); private final Map<String, String> delegateCallingConventions = Maps.newHashMap(); // Simple properties inferred about functions. private final Map<Node, AstFunctionContents> functionAnalysisResults = Maps.newHashMap(); // For convenience private final ObjectType unknownType; /** * Defer attachment of types to nodes until all type names * have been resolved. Then, we can resolve the type and attach it. */ private class DeferredSetType { final Node node; final JSType type; DeferredSetType(Node node, JSType type) { Preconditions.checkNotNull(node); Preconditions.checkNotNull(type); this.node = node; this.type = type; // Other parts of this pass may read off the node. // (like when we set the LHS of an assign with a typed RHS function.) node.setJSType(type); } void resolve(Scope scope) { node.setJSType(type.resolve(typeParsingErrorReporter, scope)); } } TypedScopeCreator(AbstractCompiler compiler) { this(compiler, compiler.getCodingConvention()); } TypedScopeCreator(AbstractCompiler compiler, CodingConvention codingConvention) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.codingConvention = codingConvention; this.typeRegistry = compiler.getTypeRegistry(); this.typeParsingErrorReporter = typeRegistry.getErrorReporter(); this.unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE); } /** * Creates a scope with all types declared. Declares newly discovered types * and type properties in the type registry. */ @Override public Scope createScope(Node root, Scope parent) { // Constructing the global scope is very different than constructing // inner scopes, because only global scopes can contain named classes that // show up in the type registry. Scope newScope = null; AbstractScopeBuilder scopeBuilder = null; if (parent == null) { JSType globalThis = typeRegistry.getNativeObjectType(JSTypeNative.GLOBAL_THIS); // Mark the main root, the externs root, and the src root // with the global this type. root.setJSType(globalThis); root.getFirstChild().setJSType(globalThis); root.getLastChild().setJSType(globalThis); // Run a first-order analysis over the syntax tree. (new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults)) .process(root.getFirstChild(), root.getLastChild()); // Find all the classes in the global scope. newScope = createInitialScope(root); GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope); scopeBuilder = globalScopeBuilder; NodeTraversal.traverse(compiler, root, scopeBuilder); } else { newScope = new Scope(parent, root); LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope); scopeBuilder = localScopeBuilder; localScopeBuilder.build(); } scopeBuilder.resolveStubDeclarations(); // Gather the properties in each function that we found in the // global scope, if that function has a @this type that we can // build properties on. for (Node functionNode : scopeBuilder.nonExternFunctions) { JSType type = functionNode.getJSType(); if (type != null && type.isFunctionType()) { FunctionType fnType = type.toMaybeFunctionType(); JSType fnThisType = fnType.getTypeOfThis(); if (!fnThisType.isUnknownType()) { NodeTraversal.traverse(compiler, functionNode.getLastChild(), scopeBuilder.new CollectProperties(fnThisType)); } } } if (parent == null) { codingConvention.defineDelegateProxyPrototypeProperties( typeRegistry, newScope, delegateProxyPrototypes, delegateCallingConventions); } newScope.setTypeResolver(scopeBuilder); return newScope; } /** * Patches a given global scope by removing variables previously declared in * a script and re-traversing a new version of that script. * * @param globalScope The global scope generated by {@code createScope}. * @param scriptRoot The script that is modified. */ void patchGlobalScope(Scope globalScope, Node scriptRoot) { // Preconditions: This is supposed to be called only on (named) SCRIPT nodes // and a global typed scope should have been generated already. Preconditions.check

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>FunctionType(tId); declareNativeType(scope, t.getInstanceType().getReferenceName(), t); declareNativeType( scope, t.getPrototype().getReferenceName(), t.getPrototype()); } private void declareNativeValueType(Scope scope, String name, JSTypeNative tId) { declareNativeType(scope, name, typeRegistry.getNativeType(tId)); } private static void declareNativeType(Scope scope, String name, JSType t) { scope.declare(name, null, t, null, false); } private static class DiscoverEnumsAndTypedefs extends AbstractShallowStatementCallback { private final JSTypeRegistry registry; DiscoverEnumsAndTypedefs(JSTypeRegistry registry) { this.registry = registry; } @Override public void visit(NodeTraversal t, Node node, Node parent) { switch (node.getType()) { case Token.VAR: for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { identifyNameNode( child, NodeUtil.getBestJSDocInfo(child)); } break; case Token.EXPR_RESULT: Node firstChild = node.getFirstChild(); if (firstChild.isAssign()) { identifyNameNode( firstChild.getFirstChild(), firstChild.getJSDocInfo()); } else { identifyNameNode( firstChild, firstChild.getJSDocInfo()); } break; } } private void identifyNameNode( Node nameNode, JSDocInfo info) { if (nameNode.isQualifiedName()) { if (info != null) { if (info.hasEnumParameterType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } else if (info.hasTypedefType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } } } } } private JSType getNativeType(JSTypeNative nativeType) { return typeRegistry.getNativeType(nativeType); } private abstract class AbstractScopeBuilder implements NodeTraversal.Callback, Scope.TypeResolver { /** * The scope that we're building. */ final Scope scope; private final List<DeferredSetType> deferredSetTypes = Lists.newArrayList(); /** * Functions that we found in the global scope and not in externs. */ private final List<Node> nonExternFunctions = Lists.newArrayList(); /** * Object literals with a @lends annotation aren't analyzed until we * reach the root of the statement they're defined in. * * This ensures that if there are any @lends annotations on the object * literals, the type on the @lends annotation resolves correctly. * * For more information, see * http://code.google.com/p/closure-compiler/issues/detail?id=314 */ private List<Node> lentObjectLiterals = null; /** * Type-less stubs. * * If at the end of traversal, we still don't have types for these * stubs, then we should declare UNKNOWN types. */ private final List<StubDeclaration> stubDeclarations = Lists.newArrayList(); /** * The current

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> source file that we're in. */ private String sourceName = null; /** * The InputId of the current node. */ private InputId inputId; private AbstractScopeBuilder(Scope scope) { this.scope = scope; } void setDeferredType(Node node, JSType type) { deferredSetTypes.add(new DeferredSetType(node, type)); } @Override public void resolveTypes() { // Resolve types and attach them to nodes. for (DeferredSetType deferred : deferredSetTypes) { deferred.resolve(scope); } // Resolve types and attach them to scope slots. Iterator<Var> vars = scope.getVars(); while (vars.hasNext()) { vars.next().resolveType(typeParsingErrorReporter); } // Tell the type registry that any remaining types // are unknown. typeRegistry.resolveTypesInScope(scope); } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); if (n.isFunction() || n.isScript()) { Preconditions.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || !parent.isFunction() || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (NodeUtil.isHoistedFunctionDeclaration(child)) { defineFunctionLiteral(child); } } } } return descend; } @Override public void visit(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); attachLiteralTypes(n); switch (n.getType()) { case Token.CALL: checkForClassDefiningCalls(t, n); checkForCallingConventionDefiningCalls(n, delegateCallingConventions); break; case Token.FUNCTION: if (t.getInput() == null || !t.getInput().isExtern()) { nonExternFunctions.add(n); } // Hoisted functions are handled during pre-traversal. if (!NodeUtil.isHoistedFunctionDeclaration(n)) { defineFunctionLiteral(n); } break; case Token.ASSIGN: // Handle initialization of properties. Node firstChild = n.getFirstChild(); if (firstChild.isGetProp() && firstChild.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), firstChild, n, firstChild.getNext()); } break; case Token.CATCH: defineCatch(n); break; case Token.VAR: defineVar(n); break; case Token.GETPROP: // Handle stubbed properties. if (parent.isExprResult() && n.isQualifiedName())

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Registry.createEnumType(name, rValue, elementsType); if (rValue != null && rValue.isObjectLit()) { // collect enum elements Node key = rValue.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (keyName == null) { // GET and SET don't have a String value; compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName, key); } key = key.getNext(); } } } if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, enumType.getElementsType()); } return enumType; } /** * Defines a typed variable. The defining node will be annotated with the * variable's type or {@code null} if its type is inferred. * @param name the defining node. It must be a {@link Token#NAME}. * @param parent the {@code name}'s parent. * @param type the variable's type. It may be {@code null}, in which case * the variable's type will be inferred. */ private void defineSlot(Node name, Node parent, JSType type) { defineSlot(name, parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is * inferred. * * Slots may be any variable or any qualified name in the global scope. * * @param n the defining NAME or GETPROP node. * @param parent the {@code n}'s parent. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. */ void defineSlot(Node n, Node parent, JSType type, boolean inferred) { Preconditions.checkArgument(inferred || type != null); // Only allow declarations of NAMEs and qualified names. // Object literal keys will have to compute their names themselves. if (n.isName()) { Preconditions.checkArgument( parent.isFunction() || parent.isVar() || parent.isParamList() || parent.isCatch()); } else { Preconditions.checkArgument( n.isGetProp() && (parent.isAssign() || parent.isExprResult())); } defineSlot(n, parent, n.getQualifiedName(), type, inferred); } /** * Defines a symbol in the current scope. * * @param n the defining NAME or GETPROP or object literal key node. * @param parent the {@code n}'s parent. * @param variableName The name that this should be known by. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. * @param inferred Whether the type is inferred or declared.

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> or not * is really really obnoxious. * * The problem is that there are two (equally valid) coding styles: * * (function() { * /* The authoritative definition of goog.bar. / * goog.bar = function() {}; * })(); * * function f() { * goog.bar(); * /* Reset goog.bar to a no-op. / * goog.bar = function() {}; * } * * In a dynamic language with first-class functions, it's very difficult * to know which one the user intended without looking at lots of * contextual information (the second example demonstrates a small case * of this, but there are some really pathological cases as well). * * The current algorithm checks if either the declaration has * JsDoc type information, or @const with a known type, * or a function literal with a name we haven't seen before. */ private boolean isQualifiedNameInferred( String qName, Node n, JSDocInfo info, Node rhsValue, JSType valueType) { if (valueType == null) { return true; } // Prototypes of constructors and interfaces are always declared. if (qName != null && qName.endsWith(".prototype")) { String className = qName.substring(0, qName.lastIndexOf(".prototype")); Var slot = scope.getSlot(className); JSType classType = slot == null ? null : slot.getType(); if (classType != null && (classType.isConstructor() || classType.isInterface())) { return false; } } boolean inferred = true; if (info != null) { inferred = !(info.hasType() || info.hasEnumParameterType() || (isConstantSymbol(info, n) && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } if (inferred && rhsValue != null && rhsValue.isFunction()) { if (info != null) { return false; } else if (!scope.isDeclared(qName, false) && n.isUnscopedQualifiedName()) { // Check if this is in a conditional block. // Functions assigned in conditional blocks are inferred. for (Node current = n.getParent(); !(current.isScript() || current.isFunction()); current = current.getParent()) { if (NodeUtil.isControlStructure(current)) { return true; } } // Check if this is assigned in an inner scope. // Functions assigned in inner scopes are inferred. AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents == null || !contents.getEscapedQualifiedNames().contains(qName)) { return false; } } } return inferred; } private boolean isConstantSymbol(JSDocInfo info, Node node) { if (info != null && info.isConstant()) { return true; } switch (node.getType()) { case Token.NAME: return NodeUtil.isConstantByConvention( compiler.getCodingConvention(), node, node.getParent()); case Token.GETPROP: return node.isQualifiedName

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>() && NodeUtil.isConstantByConvention( compiler.getCodingConvention(), node.getLastChild(), node); } return false; } /** * Find the ObjectType associated with the given slot. * @param slotName The name of the slot to find the type in. * @return An object type, or null if this slot does not contain an object. */ private ObjectType getObjectSlot(String slotName) { Var ownerVar = scope.getVar(slotName); if (ownerVar != null) { JSType ownerVarType = ownerVar.getType(); return ObjectType.cast(ownerVarType == null ? null : ownerVarType.restrictByNotNullOrUndefined()); } return null; } /** * Resolve any stub declarations to unknown types if we could not * find types for them during traversal. */ void resolveStubDeclarations() { for (StubDeclaration stub : stubDeclarations) { Node n = stub.node; Node parent = n.getParent(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); String ownerName = stub.ownerName; boolean isExtern = stub.isExtern; if (scope.isDeclared(qName, false)) { continue; } // If we see a stub property, make sure to register this property // in the type registry. ObjectType ownerType = getObjectSlot(ownerName); defineSlot(n, parent, unknownType, true); if (ownerType != null && (isExtern || ownerType.isFunctionPrototypeType())) { // If this is a stub for a prototype, just declare it // as an unknown type. These are seen often in externs. ownerType.defineInferredProperty( propName, unknownType, n); } else { typeRegistry.registerPropertyOnType( propName, ownerType == null ? unknownType : ownerType); } } } /** * Collects all declared properties in a function, and * resolves them relative to the global scope. */ private final class CollectProperties extends AbstractShallowStatementCallback { private final JSType thisType; CollectProperties(JSType thisType) { this.thisType = thisType; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isExprResult()) { Node child = n.getFirstChild(); switch (child.getType()) { case Token.ASSIGN: maybeCollectMember(child.getFirstChild(), child, child.getLastChild()); break; case Token.GETPROP: maybeCollectMember(child, child, null); break; } } } private void maybeCollectMember(Node member, Node nodeWithJsDocInfo, @Nullable Node value) { JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); // Do nothing if there is no JSDoc type info, or // if the node is not a member expression, or // if the member expression is not of the form: this.someProperty. if (info == null || !member.isGetProp() || !member.getFirstChild().isThis()) { return; } member.getFirstChild().set

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>JSType(thisType); // TODO(johnlenz): We are evaluating these values in the wrong scope: // https://code.google.com/p/closure-compiler/issues/detail?id=926 JSType thisObjectType = thisType.toObjectType(); if (thisObjectType != null) { ImmutableList<TemplateType> keys = thisObjectType.getTemplateTypeMap().getTemplateKeys(); typeRegistry.setTemplateTypeNames(keys); } JSType jsType = getDeclaredType(info, member, value); if (thisObjectType != null) { typeRegistry.clearTemplateTypeNames(); } Node name = member.getLastChild(); if (jsType != null && (name.isName() || name.isString()) && thisType.toObjectType() != null) { thisType.toObjectType().defineDeclaredProperty( name.getString(), jsType, member); } } } // end CollectProperties } /** * A stub declaration without any type information. */ private static final class StubDeclaration { private final Node node; private final boolean isExtern; private final String ownerName; private StubDeclaration(Node node, boolean isExtern, String ownerName) { this.node = node; this.isExtern = isExtern; this.ownerName = ownerName; } } /** * A shallow traversal of the global scope to build up all classes, * functions, and methods. */ private final class GlobalScopeBuilder extends AbstractScopeBuilder { private GlobalScopeBuilder(Scope scope) { super(scope); } /** * Visit a node in the global scope, and add anything it declares to the * global symbol table. * * @param t The current traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent); switch (n.getType()) { case Token.VAR: // Handle typedefs. if (n.hasOneChild()) { checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo()); } break; } } @Override void maybeDeclareQualifiedName( NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { checkForTypedef(t, n, info); super.maybeDeclareQualifiedName(t, info, n, parent, rhsValue); } /** * Handle typedefs. * @param t The current traversal. * @param candidate A qualified name node. * @param info JSDoc comments. */ private void checkForTypedef( NodeTraversal t, Node candidate, JSDocInfo info) { if (info == null || !info.hasTypedefType()) { return; } String typedef = candidate.getQualifiedName(); if (typedef == null) { return; } // TODO(nicksantos|user): This is a terrible, terrible hack // to bail out on recursive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, unknownType);

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.isGetProp()) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're building. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents != null) { for (String varName : contents.getEscapedVarNames()) { Var v = scope.getVar(varName); Preconditions.checkState(v.getScope() == scope); v.markEscaped(); } for (Multiset.Entry<String> entry : contents.getAssignedNameCounts().entrySet()) { Var v = scope.getVar(entry.getElement()); Preconditions.checkState(v.getScope() == scope); if (entry.getCount() == 1) { v.markAssignedExactlyOnce(); } } } } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) { return; } if (n.isParamList() && parent == scope.getRootNode()) { handleFunctionInputs(parent); return; } super.visit(t, n, parent); } /** Handle bleeding functions and function parameters. */ private void handleFunctionInputs(Node fnNode) { // Handle bleeding functions. Node fnNameNode = fnNode.getFirstChild(); String fnName = fnNameNode.getString(); if (!fnName.isEmpty()) { Scope.Var fnVar = scope.getVar(fnName); if (fnVar == null || // Make sure we're not touching a native function. Native // functions aren't bleeding, but may not have a declaration // node. (fnVar.getNameNode() != null && // Make sure that the function is actually bleeding by checking // if has already been declared. fnVar.getInitialValue() != fnNode)) { defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false); } } declareArguments(fnNode); } /** * Declares all of a function's arguments. */ private void declareArguments(Node functionNode) {

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> Node astParameters = functionNode.getFirstChild().getNext(); Node iifeArgumentNode = null; if (NodeUtil.isCallOrNewTarget(functionNode)) { iifeArgumentNode = functionNode.getNext(); } FunctionType functionType = JSType.toMaybeFunctionType(functionNode.getJSType()); if (functionType != null) { Node jsDocParameters = functionType.getParametersNode(); if (jsDocParameters != null) { Node jsDocParameter = jsDocParameters.getFirstChild(); for (Node astParameter : astParameters.children()) { JSType paramType = jsDocParameter == null ? unknownType : jsDocParameter.getJSType(); boolean inferred = paramType == null || paramType == unknownType; if (iifeArgumentNode != null && inferred) { String argumentName = iifeArgumentNode.getQualifiedName(); Var argumentVar = argumentName == null || scope.getParent() == null ? null : scope.getParent().getVar(argumentName); if (argumentVar != null && !argumentVar.isTypeInferred()) { paramType = argumentVar.getType(); } } if (paramType == null) { paramType = unknownType; } defineSlot(astParameter, functionNode, paramType, inferred); if (jsDocParameter != null) { jsDocParameter = jsDocParameter.getNext(); } if (iifeArgumentNode != null) { iifeArgumentNode = iifeArgumentNode.getNext(); } } } } } // end declareArguments } // end LocalScopeBuilder /** * Does a first-order function analysis that just looks at simple things * like what variables are escaped, and whether 'this' is used. */ private static class FirstOrderFunctionAnalyzer extends AbstractScopedCallback implements CompilerPass { private final AbstractCompiler compiler; private final Map<Node, AstFunctionContents> data; FirstOrderFunctionAnalyzer( AbstractCompiler compiler, Map<Node, AstFunctionContents> outParam) { this.compiler = compiler; this.data = outParam; } @Override public void process(Node externs, Node root) { if (externs == null) { NodeTraversal.traverse(compiler, root, this); } else { NodeTraversal.traverseRoots( compiler, ImmutableList.of(externs, root), this); } } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); data.put(n, new AstFunctionContents(n)); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (t.inGlobalScope()) { return; } if (n.isReturn() && n.getFirstChild() != null) { data.get(t.getScopeRoot()).recordNonEmptyReturn(); } if (t.getScopeDepth() <= 1) { // The first-order function analyzer looks at two types of variables: // // 1) Local variables that are assigned in inner scopes ("escaped vars") // // 2) Local variables that are assigned more than once. // // We treat all global variables as escaped

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> an exception if no pass with the given name exists. */ private static int findPassIndexByName( List<PassFactory> factoryList, String name) { for (int i = 0; i < factoryList.size(); i++) { if (factoryList.get(i).getName().equals(name)) { return i; } } throw new IllegalArgumentException( "No factory named '" + name + "' in the factory list"); } /** * Find the first pass provider that does not have a delegate. */ final PassConfig getBasePassConfig() { PassConfig current = this; while (current instanceof PassConfigDelegate) { current = ((PassConfigDelegate) current).delegate; } return current; } /** * Get intermediate state for a running pass config, so it can * be paused and started again later. */ protected abstract State getIntermediateState(); /** * Set the intermediate state for a pass config, to restart * a compilation process that had been previously paused. */ protected abstract void setIntermediateState(State state); /** * An implementation of PassConfig that just proxies all its method calls * into an inner class. */ static class PassConfigDelegate extends PassConfig { private final PassConfig delegate; PassConfigDelegate(PassConfig delegate) { super(delegate.options); this.delegate = delegate; } @Override protected List<PassFactory> getChecks() { return delegate.getChecks(); } @Override protected List<PassFactory> getOptimizations() { return delegate.getOptimizations(); } @Override MemoizedScopeCreator getTypedScopeCreator() { return delegate.getTypedScopeCreator(); } @Override Scope getTopScope() { return delegate.getTopScope(); } @Override protected State getIntermediateState() { return delegate.getIntermediateState(); } @Override protected void setIntermediateState(State state) { delegate.setIntermediateState(state); } } /** * Intermediate state for a running pass configuration. */ public static class State implements Serializable { private static final long serialVersionUID = 1L; final Map<String, Integer> cssNames; final Set<String> exportedNames; final CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator; final VariableMap variableMap; final VariableMap propertyMap; final VariableMap anonymousFunctionNameMap; final VariableMap stringMap; final FunctionNames functionNames; final String idGeneratorMap; public State(Map<String, Integer> cssNames, Set<String> exportedNames, CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator, VariableMap variableMap, VariableMap propertyMap, VariableMap anonymousFunctionNameMap, VariableMap stringMap, FunctionNames functionNames, String idGeneratorMap) { this.cssNames = cssNames; this.exportedNames = exportedNames; this.crossModuleIdGenerator = crossModuleIdGenerator; this.variableMap = variableMap; this.propertyMap = propertyMap; this.anonymousFunctionNameMap = anonymousFunctionNameMap; this.stringMap = stringMap; this.idGeneratorMap = idGeneratorMap; this.functionNames = functionNames; } } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.io.Serializable; /** * A property slot of an object. * @author nicksantos@google.com (Nick Santos) */ public final class Property implements Serializable, StaticSlot<JSType>, StaticReference<JSType> { private static final long serialVersionUID = 1L; /** * Property's name. */ private final String name; /** * Property's type. */ private JSType type; /** * Whether the property's type is inferred. */ private final boolean inferred; /** * The node corresponding to this property, e.g., a GETPROP node that * declares this property. */ private Node propertyNode; /** The JSDocInfo for this property. */ private JSDocInfo docInfo = null; Property(String name, JSType type, boolean inferred, Node propertyNode) { this.name = name; this.type = type; this.inferred = inferred; this.propertyNode = propertyNode; } @Override public String getName() { return name; } @Override public Node getNode() { return propertyNode; } @Override public StaticSourceFile getSourceFile() { return propertyNode ==

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> null ? null : propertyNode.getStaticSourceFile(); } @Override public Property getSymbol() { return this; } @Override public Property getDeclaration() { return propertyNode == null ? null : this; } @Override public JSType getType() { return type; } @Override public boolean isTypeInferred() { return inferred; } boolean isFromExterns() { return propertyNode == null ? false : propertyNode.isFromExterns(); } void setType(JSType type) { this.type = type; } @Override public JSDocInfo getJSDocInfo() { return this.docInfo; } void setJSDocInfo(JSDocInfo info) { this.docInfo = info; } public void setNode(Node n) { this.propertyNode = n; } }

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>, new Node(Token.PARAM_LIST), null); this.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); } /** Creates an instance for a function that is an interface. */ static FunctionType forInterface( JSTypeRegistry registry, String name, Node source, TemplateTypeMap typeParameters) { return new FunctionType(registry, name, source, typeParameters); } @Override public boolean isInstanceType() { // The universal constructor is its own instance, bizarrely. It overrides // getConstructor() appropriately when it's declared. return this == registry.getNativeType(U2U_CONSTRUCTOR_TYPE); } @Override public boolean isConstructor() { return kind == Kind.CONSTRUCTOR; } @Override public boolean isInterface() { return kind == Kind.INTERFACE; } @Override public boolean isOrdinaryFunction() { return kind == Kind.ORDINARY; } /** * When a class B inherits from A and A is annotated as a struct, then B * automatically gets the annotation, even if B's constructor is not * explicitly annotated. */ public boolean makesStructs() { if (!isConstructor()) { return false; } if (propAccess == PropAccess.STRUCT) { return true; } FunctionType superc = getSuperClassConstructor(); if (superc != null && superc.makesStructs()) { setStruct(); return true; } return false; } /** * When a class B inherits from A and A is annotated as a dict, then B * automatically gets the annotation, even if B's constructor is not * explicitly annotated. */ public boolean makesDicts() { if (!isConstructor()) { return false; } if (propAccess == PropAccess.DICT) { return true; } FunctionType superc = getSuperClassConstructor(); if (superc != null && superc.makesDicts()) { setDict(); return true; } return false; } public void setStruct() { propAccess = PropAccess.STRUCT; } public void setDict() { propAccess = PropAccess.DICT; } @Override public FunctionType toMaybeFunctionType() { return this; } @Override public boolean canBeCalled() { return true; } public boolean hasImplementedInterfaces() { if (!implementedInterfaces.isEmpty()){ return true; } FunctionType superCtor = isConstructor() ? getSuperClassConstructor() : null; if (superCtor != null) { return superCtor.hasImplementedInterfaces(); } return false; } public Iterable<Node> getParameters() { Node n = getParametersNode(); if (n != null) { return n.children(); } else { return Collections.emptySet(); } } /** Gets an LP node that contains all params. May be null. */ public Node getParametersNode() { return call.parameters; } /** Gets the minimum number of arguments that this function requires. */ public int getMinArguments() { // NOTE(nicksantos): There are some native functions that have optional //

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> parameters before required parameters. This algorithm finds the position // of the last required parameter. int i = 0; int min = 0; for (Node n : getParameters()) { i++; if (!n.isOptionalArg() && !n.isVarArgs()) { min = i; } } return min; } /** * Gets the maximum number of arguments that this function requires, * or Integer.MAX_VALUE if this is a variable argument function. */ public int getMaxArguments() { Node params = getParametersNode(); if (params != null) { Node lastParam = params.getLastChild(); if (lastParam == null || !lastParam.isVarArgs()) { return params.getChildCount(); } } return Integer.MAX_VALUE; } public JSType getReturnType() { return call.returnType; } public boolean isReturnTypeInferred() { return call.returnTypeInferred; } /** Gets the internal arrow type. For use by subclasses only. */ ArrowType getInternalArrowType() { return call; } @Override public Property getSlot(String name) { if ("prototype".equals(name)) { // Lazy initialization of the prototype field. getPrototype(); return prototypeSlot; } else { return super.getSlot(name); } } /** * Includes the prototype iff someone has created it. We do not want * to expose the prototype for ordinary functions. */ @Override public Set<String> getOwnPropertyNames() { if (prototypeSlot == null) { return super.getOwnPropertyNames(); } else { Set<String> names = Sets.newHashSet("prototype"); names.addAll(super.getOwnPropertyNames()); return names; } } /** * Gets the {@code prototype} property of this function type. This is * equivalent to {@code (ObjectType) getPropertyType("prototype")}. */ public ObjectType getPrototype() { // lazy initialization of the prototype field if (prototypeSlot == null) { String refName = getReferenceName(); if (refName == null) { // Someone is trying to access the prototype of a structural function. // We don't want to give real properties to this prototype, because // then it would propagate to all structural functions. setPrototypeNoCheck( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE), null); } else { setPrototype( new PrototypeObjectType( registry, getReferenceName() + ".prototype", registry.getNativeObjectType(OBJECT_TYPE), isNativeObjectType(), null), null); } } return (ObjectType) prototypeSlot.getType(); } /** * Sets the prototype, creating the prototype object from the given * base type. * @param baseType The base type. */ public void setPrototypeBasedOn(ObjectType baseType) { setPrototypeBasedOn(baseType, null); } void setPrototypeBasedOn(ObjectType baseType, Node propertyNode) { // This is a bit weird. We need to successfully handle these // two cases: // Foo.prototype = new Bar(); // and // Foo.prototype = {baz: 3}; // In the first case

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } return interfaces; } private void addRelatedInterfaces(ObjectType instance, Set<ObjectType> set) { FunctionType constructor = instance.getConstructor(); if (constructor != null) { if (!constructor.isInterface()) { return; } set.add(instance); for (ObjectType interfaceType : instance.getCtorExtendedInterfaces()) { addRelatedInterfaces(interfaceType, set); } } } /** Returns interfaces implemented directly by a class or its superclass. */ public Iterable<ObjectType> getImplementedInterfaces() { FunctionType superCtor = isConstructor() ? getSuperClassConstructor() : null; if (superCtor == null) { return implementedInterfaces; } else { return Iterables.concat( implementedInterfaces, superCtor.getImplementedInterfaces()); } } /** Returns interfaces directly implemented by the class. */ public Iterable<ObjectType> getOwnImplementedInterfaces() { return implementedInterfaces; } public void setImplementedInterfaces(List<ObjectType> implementedInterfaces) { if (isConstructor()) { // Records this type for each implemented interface. for (ObjectType type : implementedInterfaces) { registry.registerTypeImplementingInterface(this, type); typeOfThis.extendTemplateTypeMap(type.getTemplateTypeMap()); } this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces); } else { throw new UnsupportedOperationException(); } } /** * Returns all extended interfaces declared by an interfaces or its super- * interfaces. If this is called before all types are resolved, it may return * an incomplete set. */ public Iterable<ObjectType> getAllExtendedInterfaces() { // Store them in a linked hash set, so that the compile job is // deterministic. Set<ObjectType> extendedInterfaces = Sets.newLinkedHashSet(); for (ObjectType interfaceType : getExtendedInterfaces()) { addRelatedExtendedInterfaces(interfaceType, extendedInterfaces); } return extendedInterfaces; } private void addRelatedExtendedInterfaces(ObjectType instance, Set<ObjectType> set) { FunctionType constructor = instance.getConstructor(); if (constructor != null) { set.add(instance); for (ObjectType interfaceType : constructor.getExtendedInterfaces()) { addRelatedExtendedInterfaces(interfaceType, set); } } } /** Returns interfaces directly extended by an interface */ public Iterable<ObjectType> getExtendedInterfaces() { return extendedInterfaces; } /** Returns the number of interfaces directly extended by an interface */ public int getExtendedInterfacesCount() { return extendedInterfaces.size(); } public void setExtendedInterfaces(List<ObjectType> extendedInterfaces) throws UnsupportedOperationException { if (isInterface()) { this.extendedInterfaces = ImmutableList.copyOf(extendedInterfaces); for (ObjectType extendedInterface : this.extendedInterfaces) { typeOfThis.extendTemplateTypeMap( extendedInterface.getTemplateTypeMap()); } } else { throw new UnsupportedOperationException(); } } @Override public JSType getPropertyType(String name) { if (!hasOwnProperty(name)) { // Define the "call", "apply", and "bind" functions lazily. boolean isCall = "call".equals(name); boolean isBind = "bind".equals(name); if (isCall || isBind) { defineDeclaredProperty(name, getCallOrBindSignature(

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>Arg.isOptionalArg() || firstArg.isVarArgs()) { thisTypeNode.setOptionalArg(true); } } builder.withParamsNode(params); } return builder.build(); } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { if ("prototype".equals(name)) { ObjectType objType = type.toObjectType(); if (objType != null) { if (prototypeSlot != null && objType.isEquivalentTo(prototypeSlot.getType())) { return true; } setPrototypeBasedOn(objType, propertyNode); return true; } else { return false; } } return super.defineProperty(name, type, inferred, propertyNode); } /** * Computes the supremum or infimum of two functions. * Because sup() and inf() share a lot of logic for functions, we use * a single helper. * @param leastSuper If true, compute the supremum of {@code this} with * {@code that}. Otherwise, compute the infimum. * @return The least supertype or greatest subtype. */ FunctionType supAndInfHelper(FunctionType that, boolean leastSuper) { // NOTE(nicksantos): When we remove the unknown type, the function types // form a lattice with the universal constructor at the top of the lattice, // and the LEAST_FUNCTION_TYPE type at the bottom of the lattice. // // When we introduce the unknown type, it's much more difficult to make // heads or tails of the partial ordering of types, because there's no // clear hierarchy between the different components (parameter types and // return types) in the ArrowType. // // Rather than make the situation more complicated by introducing new // types (like unions of functions), we just fallback on the simpler // approach of getting things right at the top and the bottom of the // lattice. // // If there are unknown parameters or return types making things // ambiguous, then sup(A, B) is always the top function type, and // inf(A, B) is always the bottom function type. Preconditions.checkNotNull(that); if (isEquivalentTo(that)) { return this; } // If these are ordinary functions, then merge them. // Don't do this if any of the params/return // values are unknown, because then there will be cycles in // their local lattice and they will merge in weird ways. if (isOrdinaryFunction() && that.isOrdinaryFunction() && !this.call.hasUnknownParamsOrReturn() && !that.call.hasUnknownParamsOrReturn()) { // Check for the degenerate case, but double check // that there's not a cycle. boolean isSubtypeOfThat = isSubtype(that); boolean isSubtypeOfThis = that.isSubtype(this); if (isSubtypeOfThat && !isSubtypeOfThis) { return leastSuper ? that : this; } else if (isSubtypeOfThis && !isSubtypeOfThat) { return leastSuper ? this : that; } // Merge the two functions component-wise. FunctionType merged = tryMergeFunctionPiecewise(that,

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> ObjectType foundType = null; if (type.hasProperty(propertyName)) { foundType = type; } for (ObjectType interfaceType : type.getCtorExtendedInterfaces()) { if (interfaceType.hasProperty(propertyName)) { foundType = getTopDefiningInterface(interfaceType, propertyName); } } return foundType; } /** * Given a constructor or an interface type and a property, finds the * top-most superclass that has the property defined (including this * constructor). */ public ObjectType getTopMostDefiningType(String propertyName) { Preconditions.checkState(isConstructor() || isInterface()); Preconditions.checkArgument(getInstanceType().hasProperty(propertyName)); FunctionType ctor = this; if (isInterface()) { return getTopDefiningInterface(getInstanceType(), propertyName); } ObjectType topInstanceType = null; do { topInstanceType = ctor.getInstanceType(); ctor = ctor.getSuperClassConstructor(); } while (ctor != null && ctor.getPrototype().hasProperty(propertyName)); return topInstanceType; } /** * Two function types are equal if their signatures match. Since they don't * have signatures, two interfaces are equal if their names match. */ boolean checkFunctionEquivalenceHelper( FunctionType that, EquivalenceMethod eqMethod) { if (isConstructor()) { if (that.isConstructor()) { return this == that; } return false; } if (isInterface()) { if (that.isInterface()) { return getReferenceName().equals(that.getReferenceName()); } return false; } if (that.isInterface()) { return false; } return typeOfThis.checkEquivalenceHelper(that.typeOfThis, eqMethod) && call.checkArrowEquivalenceHelper(that.call, eqMethod); } @Override public int hashCode() { return isInterface() ? getReferenceName().hashCode() : call.hashCode(); } public boolean hasEqualCallType(FunctionType otherType) { return this.call.checkArrowEquivalenceHelper( otherType.call, EquivalenceMethod.IDENTITY); } /** * Informally, a function is represented by * {@code function (params): returnType} where the {@code params} is a comma * separated list of types, the first one being a special * {@code this:T} if the function expects a known type for {@code this}. */ @Override String toStringHelper(boolean forAnnotations) { if (!isPrettyPrint() || this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return "Function"; } setPrettyPrint(false); StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !(typeOfThis instanceof UnknownType); if (hasKnownTypeOfThis) { if (isConstructor()) { b.append("new:"); } else { b.append("this:"); } b.append(typeOfThis.toStringHelper(forAnnotations)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", ");

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> } Node p = call.parameters.getFirstChild(); appendArgString(b, p, forAnnotations); p = p.getNext(); while (p != null) { b.append(", "); appendArgString(b, p, forAnnotations); p = p.getNext(); } } b.append("): "); b.append(call.returnType.toStringHelper(forAnnotations)); setPrettyPrint(true); return b.toString(); } private void appendArgString( StringBuilder b, Node p, boolean forAnnotations) { if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType(), forAnnotations); } else if (p.isOptionalArg()) { appendOptionalArgString(b, p.getJSType(), forAnnotations); } else { b.append(p.getJSType().toStringHelper(forAnnotations)); } } /** Gets the string representation of a var args param. */ private void appendVarArgsString(StringBuilder builder, JSType paramType, boolean forAnnotations) { if (paramType.isUnionType()) { // Remove the optionality from the var arg. paramType = paramType.toMaybeUnionType().getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append("...[").append( paramType.toStringHelper(forAnnotations)).append("]"); } /** Gets the string representation of an optional param. */ private void appendOptionalArgString( StringBuilder builder, JSType paramType, boolean forAnnotations) { if (paramType.isUnionType()) { // Remove the optionality from the var arg. paramType = paramType.toMaybeUnionType().getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append(paramType.toStringHelper(forAnnotations)).append("="); } /** * A function is a subtype of another if their call methods are related via * subtyping and {@code this} is a subtype of {@code that} with regard to * the prototype chain. */ @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } if (that.isFunctionType()) { FunctionType other = that.toMaybeFunctionType(); if (other.isInterface()) { // Any function can be assigned to an interface function. return true; } if (isInterface()) { // An interface function cannot be assigned to anything. return false; } // If functionA is a subtype of functionB, then their "this" types // should be contravariant. However, this causes problems because // of the way we enforce overrides. Because function(this:SubFoo) // is not a subtype of function(this:Foo), our override check treats // this as an error. Let's punt on all this for now. // TODO(nicksantos): fix this. boolean treatThisTypesAsCovariant = // An interface 'this'-type is non-restrictive. // In practical terms, if C implements I, and I has a method m, // then any m doesn't necessarily have

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> to C#m's 'this' // type doesn't need to match I. (other.typeOfThis.toObjectType() != null && other.typeOfThis.toObjectType().getConstructor() != null && other.typeOfThis.toObjectType().getConstructor().isInterface()) || // If one of the 'this' types is covariant of the other, // then we'll treat them as covariant (see comment above). other.typeOfThis.isSubtype(this.typeOfThis) || this.typeOfThis.isSubtype(other.typeOfThis); return treatThisTypesAsCovariant && this.call.isSubtype(other.call); } return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseFunctionType(this); } @Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) { return visitor.caseFunctionType(this, that); } /** * Gets the type of instance of this function. * @throws IllegalStateException if this function is not a constructor * (see {@link #isConstructor()}). */ public ObjectType getInstanceType() { Preconditions.checkState(hasInstanceType()); return typeOfThis.toObjectType(); } /** * Sets the instance type. This should only be used for special * native types. */ void setInstanceType(ObjectType instanceType) { typeOfThis = instanceType; } /** * Returns whether this function type has an instance type. */ public boolean hasInstanceType() { return isConstructor() || isInterface(); } /** * Gets the type of {@code this} in this function. */ @Override public JSType getTypeOfThis() { return typeOfThis.isEmptyType() ? registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE) : typeOfThis; } /** * Gets the source node or null if this is an unknown function. */ public Node getSource() { return source; } /** * Sets the source node. */ public void setSource(Node source) { if (prototypeSlot != null) { // NOTE(bashir): On one hand when source is null we want to drop any // references to old nodes retained in prototypeSlot. On the other hand // we cannot simply drop prototypeSlot, so we retain all information // except the propertyNode for which we use an approximation! These // details mostly matter in hot-swap passes. if (source == null || prototypeSlot.getNode() == null) { prototypeSlot = new Property(prototypeSlot.getName(), prototypeSlot.getType(), prototypeSlot.isTypeInferred(), source); } } this.source = source; } /** Adds a type to the list of subtypes for this type. */ private void addSubType(FunctionType subType) { if (subTypes == null) { subTypes = Lists.newArrayList(); } subTypes.add(subType); } @Override public void clearCachedValues() { super.clearCachedValues(); if (subTypes != null) { for (FunctionType subType : subTypes)

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS> { subType.clearCachedValues(); } } if (!isNativeObjectType()) { if (hasInstanceType()) { getInstanceType().clearCachedValues(); } if (prototypeSlot != null) { ((ObjectType) prototypeSlot.getType()).clearCachedValues(); } } } /** * Returns a list of types that are subtypes of this type. This is only valid * for constructor functions, and may be null. This allows a downward * traversal of the subtype graph. */ public List<FunctionType> getSubTypes() { return subTypes; } @Override public boolean hasCachedValues() { return prototypeSlot != null || super.hasCachedValues(); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); call = (ArrowType) safeResolve(call, t, scope); if (prototypeSlot != null) { prototypeSlot.setType( safeResolve(prototypeSlot.getType(), t, scope)); } // Warning about typeOfThis if it doesn't resolve to an ObjectType // is handled further upstream. // // TODO(nicksantos): Handle this correctly if we have a UnionType. // // TODO(nicksantos): In ES3, the run-time coerces "null" to the global // activation object. In ES5, it leaves it as null. Just punt on this // issue for now by coercing out null. This is complicated by the // fact that when most people write @this {Foo}, they really don't // mean "nullable Foo". For certain tags (like @extends) we de-nullify // the name for them. JSType maybeTypeOfThis = safeResolve(typeOfThis, t, scope); if (maybeTypeOfThis != null) { maybeTypeOfThis = maybeTypeOfThis.restrictByNotNullOrUndefined(); } if (maybeTypeOfThis instanceof ObjectType) { typeOfThis = maybeTypeOfThis; } boolean changed = false; ImmutableList.Builder<ObjectType> resolvedInterfaces = ImmutableList.builder(); for (ObjectType iface : implementedInterfaces) { ObjectType resolvedIface = (ObjectType) iface.resolve(t, scope); resolvedInterfaces.add(resolvedIface); changed |= (resolvedIface != iface); } if (changed) { implementedInterfaces = resolvedInterfaces.build(); } if (subTypes != null) { for (int i = 0; i < subTypes.size(); i++) { subTypes.set( i, JSType.toMaybeFunctionType(subTypes.get(i).resolve(t, scope))); } } return super.resolveInternal(t, scope); } @Override public String toDebugHashCodeString() { if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return super.toDebugHashCodeString(); } StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(getDebugHashCodeStringOf

Closure, 112

<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB> JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap() .getTemplateKeys(); if (keys.isEmpty()) { return false; } // Try to infer the template types <CHANGES> Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(fnType, n); <CHANGEE> // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } <FILEE> <SCANS>(typeOfThis)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); while (p != null) { b.append(", "); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); } } b.append(")"); b.append(": "); b.append(getDebugHashCodeStringOf(call.returnType)); return b.toString(); } private String getDebugHashCodeStringOf(JSType type) { if (type == this) { return "me"; } else { return type.toDebugHashCodeString(); } } /** Create a new constructor with the parameters and return type stripped. */ public FunctionType cloneWithoutArrowType() { FunctionType result = new FunctionType( registry, getReferenceName(), source, registry.createArrowType(null, null), getInstanceType(), null, true, false); result.setPrototypeBasedOn(getInstanceType()); return result; } @Override public boolean hasAnyTemplateTypesInternal() { return getTemplateTypeMap().numUnfilledTemplateKeys() > 0 || typeOfThis.hasAnyTemplateTypes() || call.hasAnyTemplateTypes(); } }